Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / ext-all-debug.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.DomHelper
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11  * from your DOM building code.</p>
12  *
13  * <p><b><u>DomHelper element specification object</u></b></p>
14  * <p>A specification object is used when creating elements. Attributes of this object
15  * are assumed to be element attributes, except for 4 special attributes:
16  * <div class="mdetail-params"><ul>
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19  * same kind of element definition objects to be created and appended. These can be nested
20  * as deep as you want.</div></li>
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22  * This will end up being either the "class" attribute on a HTML fragment or className
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
25  * </ul></div></p>
26  *
27  * <p><b><u>Insertion methods</u></b></p>
28  * <p>Commonly used insertion methods:
29  * <div class="mdetail-params"><ul>
30  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
36  * </ul></div></p>
37  *
38  * <p><b><u>Example</u></b></p>
39  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40  * element with id <tt>'my-div'</tt>:<br>
41  <pre><code>
42 var dh = Ext.DomHelper; // create shorthand alias
43 // specification object
44 var spec = {
45     id: 'my-ul',
46     tag: 'ul',
47     cls: 'my-list',
48     // append children after creating
49     children: [     // may also specify 'cn' instead of 'children'
50         {tag: 'li', id: 'item0', html: 'List Item 0'},
51         {tag: 'li', id: 'item1', html: 'List Item 1'},
52         {tag: 'li', id: 'item2', html: 'List Item 2'}
53     ]
54 };
55 var list = dh.append(
56     'my-div', // the context element 'my-div' can either be the id or the actual node
57     spec      // the specification object
58 );
59  </code></pre></p>
60  * <p>Element creation specification parameters in this class may also be passed as an Array of
61  * specification objects. This can be used to insert multiple sibling nodes into an existing
62  * container very efficiently. For example, to add more list items to the example above:<pre><code>
63 dh.append('my-ul', [
64     {tag: 'li', id: 'item3', html: 'List Item 3'},
65     {tag: 'li', id: 'item4', html: 'List Item 4'}
66 ]);
67  * </code></pre></p>
68  *
69  * <p><b><u>Templating</u></b></p>
70  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72  * insert new elements. Revisiting the example above, we could utilize templating this time:
73  * <pre><code>
74 // create the node
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
76 // get template
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
78
79 for(var i = 0; i < 5, i++){
80     tpl.append(list, [i]); // use template to append to the actual node
81 }
82  * </code></pre></p>
83  * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
85
86 var tpl = new Ext.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
89  * </code></pre></p>
90  *
91  * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
93
94 var tpl = new Ext.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
96     id: 'link1',
97     url: 'http://www.jackslocum.com/',
98     text: "Jack&#39;s Site"
99 });
100 tpl.append('blog-roll', {
101     id: 'link2',
102     url: 'http://www.dustindiaz.com/',
103     text: "Dustin&#39;s Site"
104 });
105  * </code></pre></p>
106  *
107  * <p><b><u>Compiling Templates</u></b></p>
108  * <p>Templates are applied using regular expressions. The performance is great, but if
109  * you are adding a bunch of DOM elements using the same template, you can increase
110  * performance even further by {@link Ext.Template#compile "compiling"} the template.
111  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112  * broken up at the different variable points and a dynamic function is created and eval'ed.
113  * The generated function performs string concatenation of these parts and the passed
114  * variables instead of using regular expressions.
115  * <pre><code>
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
117
118 var tpl = new Ext.DomHelper.createTemplate(html);
119 tpl.compile();
120
121 //... use template like normal
122  * </code></pre></p>
123  *
124  * <p><b><u>Performance Boost</u></b></p>
125  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126  * of DOM can significantly boost performance.</p>
127  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129  * results in the creation of a text node. Usage:</p>
130  * <pre><code>
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
132  * </code></pre>
133  * @singleton
134  */
135 Ext.DomHelper = function(){
136     var tempTableEl = null,
137         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
138         tableRe = /^table|tbody|tr|td$/i,
139         pub,
140         // kill repeat to save bytes
141         afterbegin = 'afterbegin',
142         afterend = 'afterend',
143         beforebegin = 'beforebegin',
144         beforeend = 'beforeend',
145         ts = '<table>',
146         te = '</table>',
147         tbs = ts+'<tbody>',
148         tbe = '</tbody>'+te,
149         trs = tbs + '<tr>',
150         tre = '</tr>'+tbe;
151
152     // private
153     function doInsert(el, o, returnElement, pos, sibling, append){
154         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
155         return returnElement ? Ext.get(newNode, true) : newNode;
156     }
157
158     // build as innerHTML where available
159     function createHtml(o){
160         var b = '',
161             attr,
162             val,
163             key,
164             keyVal,
165             cn;
166
167         if(Ext.isString(o)){
168             b = o;
169         } else if (Ext.isArray(o)) {
170             for (var i=0; i < o.length; i++) {
171                 if(o[i]) {
172                     b += createHtml(o[i]);
173                 }
174             };
175         } else {
176             b += '<' + (o.tag = o.tag || 'div');
177             Ext.iterate(o, function(attr, val){
178                 if(!/tag|children|cn|html$/i.test(attr)){
179                     if (Ext.isObject(val)) {
180                         b += ' ' + attr + '="';
181                         Ext.iterate(val, function(key, keyVal){
182                             b += key + ':' + keyVal + ';';
183                         });
184                         b += '"';
185                     }else{
186                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
187                     }
188                 }
189             });
190             // Now either just close the tag or try to add children and close the tag.
191             if (emptyTags.test(o.tag)) {
192                 b += '/>';
193             } else {
194                 b += '>';
195                 if ((cn = o.children || o.cn)) {
196                     b += createHtml(cn);
197                 } else if(o.html){
198                     b += o.html;
199                 }
200                 b += '</' + o.tag + '>';
201             }
202         }
203         return b;
204     }
205
206     function ieTable(depth, s, h, e){
207         tempTableEl.innerHTML = [s, h, e].join('');
208         var i = -1,
209             el = tempTableEl,
210             ns;
211         while(++i < depth){
212             el = el.firstChild;
213         }
214 //      If the result is multiple siblings, then encapsulate them into one fragment.
215         if(ns = el.nextSibling){
216             var df = document.createDocumentFragment();
217             while(el){
218                 ns = el.nextSibling;
219                 df.appendChild(el);
220                 el = ns;
221             }
222             el = df;
223         }
224         return el;
225     }
226
227     /**
228      * @ignore
229      * Nasty code for IE's broken table implementation
230      */
231     function insertIntoTable(tag, where, el, html) {
232         var node,
233             before;
234
235         tempTableEl = tempTableEl || document.createElement('div');
236
237         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
238            !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
239             return;
240         }
241         before = where == beforebegin ? el :
242                  where == afterend ? el.nextSibling :
243                  where == afterbegin ? el.firstChild : null;
244
245         if (where == beforebegin || where == afterend) {
246             el = el.parentNode;
247         }
248
249         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
250             node = ieTable(4, trs, html, tre);
251         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
252                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
253             node = ieTable(3, tbs, html, tbe);
254         } else {
255             node = ieTable(2, ts, html, te);
256         }
257         el.insertBefore(node, before);
258         return node;
259     }
260
261
262     pub = {
263         /**
264          * Returns the markup for the passed Element(s) config.
265          * @param {Object} o The DOM object spec (and children)
266          * @return {String}
267          */
268         markup : function(o){
269             return createHtml(o);
270         },
271         
272         /**
273          * Applies a style specification to an element.
274          * @param {String/HTMLElement} el The element to apply styles to
275          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
276          * a function which returns such a specification.
277          */
278         applyStyles : function(el, styles){
279             if(styles){
280                 var i = 0,
281                     len,
282                     style;
283
284                 el = Ext.fly(el);
285                 if(Ext.isFunction(styles)){
286                     styles = styles.call();
287                 }
288                 if(Ext.isString(styles)){
289                     styles = styles.trim().split(/\s*(?::|;)\s*/);
290                     for(len = styles.length; i < len;){
291                         el.setStyle(styles[i++], styles[i++]);
292                     }
293                 }else if (Ext.isObject(styles)){
294                     el.setStyle(styles);
295                 }
296             }
297         },
298
299         /**
300          * Inserts an HTML fragment into the DOM.
301          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
302          * @param {HTMLElement} el The context element
303          * @param {String} html The HTML fragment
304          * @return {HTMLElement} The new node
305          */
306         insertHtml : function(where, el, html){
307             var hash = {},
308                 hashVal,
309                 setStart,
310                 range,
311                 frag,
312                 rangeEl,
313                 rs;
314
315             where = where.toLowerCase();
316             // add these here because they are used in both branches of the condition.
317             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
318             hash[afterend] = ['AfterEnd', 'nextSibling'];
319
320             if (el.insertAdjacentHTML) {
321                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
322                     return rs;
323                 }
324                 // add these two to the hash.
325                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
326                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
327                 if ((hashVal = hash[where])) {
328                     el.insertAdjacentHTML(hashVal[0], html);
329                     return el[hashVal[1]];
330                 }
331             } else {
332                 range = el.ownerDocument.createRange();
333                 setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');
334                 if (hash[where]) {
335                     range[setStart](el);
336                     frag = range.createContextualFragment(html);
337                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
338                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
339                 } else {
340                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
341                     if (el.firstChild) {
342                         range[setStart](el[rangeEl]);
343                         frag = range.createContextualFragment(html);
344                         if(where == afterbegin){
345                             el.insertBefore(frag, el.firstChild);
346                         }else{
347                             el.appendChild(frag);
348                         }
349                     } else {
350                         el.innerHTML = html;
351                     }
352                     return el[rangeEl];
353                 }
354             }
355             throw 'Illegal insertion point -> "' + where + '"';
356         },
357
358         /**
359          * Creates new DOM element(s) and inserts them before el.
360          * @param {Mixed} el The context element
361          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
362          * @param {Boolean} returnElement (optional) true to return a Ext.Element
363          * @return {HTMLElement/Ext.Element} The new node
364          */
365         insertBefore : function(el, o, returnElement){
366             return doInsert(el, o, returnElement, beforebegin);
367         },
368
369         /**
370          * Creates new DOM element(s) and inserts them after el.
371          * @param {Mixed} el The context element
372          * @param {Object} o The DOM object spec (and children)
373          * @param {Boolean} returnElement (optional) true to return a Ext.Element
374          * @return {HTMLElement/Ext.Element} The new node
375          */
376         insertAfter : function(el, o, returnElement){
377             return doInsert(el, o, returnElement, afterend, 'nextSibling');
378         },
379
380         /**
381          * Creates new DOM element(s) and inserts them as the first child of el.
382          * @param {Mixed} el The context element
383          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
384          * @param {Boolean} returnElement (optional) true to return a Ext.Element
385          * @return {HTMLElement/Ext.Element} The new node
386          */
387         insertFirst : function(el, o, returnElement){
388             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
389         },
390
391         /**
392          * Creates new DOM element(s) and appends them to el.
393          * @param {Mixed} el The context element
394          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
395          * @param {Boolean} returnElement (optional) true to return a Ext.Element
396          * @return {HTMLElement/Ext.Element} The new node
397          */
398         append : function(el, o, returnElement){
399             return doInsert(el, o, returnElement, beforeend, '', true);
400         },
401
402         /**
403          * Creates new DOM element(s) and overwrites the contents of el with them.
404          * @param {Mixed} el The context element
405          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
406          * @param {Boolean} returnElement (optional) true to return a Ext.Element
407          * @return {HTMLElement/Ext.Element} The new node
408          */
409         overwrite : function(el, o, returnElement){
410             el = Ext.getDom(el);
411             el.innerHTML = createHtml(o);
412             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
413         },
414
415         createHtml : createHtml
416     };
417     return pub;
418 }();/**
419  * @class Ext.DomHelper
420  */
421 Ext.apply(Ext.DomHelper,
422 function(){
423         var pub,
424                 afterbegin = 'afterbegin',
425         afterend = 'afterend',
426         beforebegin = 'beforebegin',
427         beforeend = 'beforeend';
428
429         // private
430     function doInsert(el, o, returnElement, pos, sibling, append){
431         el = Ext.getDom(el);
432         var newNode;
433         if (pub.useDom) {
434             newNode = createDom(o, null);
435             if (append) {
436                     el.appendChild(newNode);
437             } else {
438                         (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
439             }
440         } else {
441             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
442         }
443         return returnElement ? Ext.get(newNode, true) : newNode;
444     }
445
446         // build as dom
447     /** @ignore */
448     function createDom(o, parentNode){
449         var el,
450                 doc = document,
451                 useSet,
452                 attr,
453                 val,
454                 cn;
455
456         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
457             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
458                 Ext.each(o, function(v) {
459                 createDom(v, el);
460             });
461         } else if (Ext.isString(o)) {         // Allow a string as a child spec.
462             el = doc.createTextNode(o);
463         } else {
464             el = doc.createElement( o.tag || 'div' );
465             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
466             Ext.iterate(o, function(attr, val){
467                 if(!/tag|children|cn|html|style/.test(attr)){
468                         if(attr == 'cls'){
469                             el.className = val;
470                         }else{
471                         if(useSet){
472                             el.setAttribute(attr, val);
473                         }else{
474                             el[attr] = val;
475                         }
476                         }
477                 }
478             });
479             Ext.DomHelper.applyStyles(el, o.style);
480
481             if ((cn = o.children || o.cn)) {
482                 createDom(cn, el);
483             } else if (o.html) {
484                 el.innerHTML = o.html;
485             }
486         }
487         if(parentNode){
488            parentNode.appendChild(el);
489         }
490         return el;
491     }
492
493         pub = {
494                 /**
495              * Creates a new Ext.Template from the DOM object spec.
496              * @param {Object} o The DOM object spec (and children)
497              * @return {Ext.Template} The new template
498              */
499             createTemplate : function(o){
500                 var html = Ext.DomHelper.createHtml(o);
501                 return new Ext.Template(html);
502             },
503
504                 /** True to force the use of DOM instead of html fragments @type Boolean */
505             useDom : false,
506
507             /**
508              * Creates new DOM element(s) and inserts them before el.
509              * @param {Mixed} el The context element
510              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
511              * @param {Boolean} returnElement (optional) true to return a Ext.Element
512              * @return {HTMLElement/Ext.Element} The new node
513          * @hide (repeat)
514              */
515             insertBefore : function(el, o, returnElement){
516                 return doInsert(el, o, returnElement, beforebegin);
517             },
518
519             /**
520              * Creates new DOM element(s) and inserts them after el.
521              * @param {Mixed} el The context element
522              * @param {Object} o The DOM object spec (and children)
523              * @param {Boolean} returnElement (optional) true to return a Ext.Element
524              * @return {HTMLElement/Ext.Element} The new node
525          * @hide (repeat)
526              */
527             insertAfter : function(el, o, returnElement){
528                 return doInsert(el, o, returnElement, afterend, 'nextSibling');
529             },
530
531             /**
532              * Creates new DOM element(s) and inserts them as the first child of el.
533              * @param {Mixed} el The context element
534              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
535              * @param {Boolean} returnElement (optional) true to return a Ext.Element
536              * @return {HTMLElement/Ext.Element} The new node
537          * @hide (repeat)
538              */
539             insertFirst : function(el, o, returnElement){
540                 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
541             },
542
543             /**
544              * Creates new DOM element(s) and appends them to el.
545              * @param {Mixed} el The context element
546              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
547              * @param {Boolean} returnElement (optional) true to return a Ext.Element
548              * @return {HTMLElement/Ext.Element} The new node
549          * @hide (repeat)
550              */
551             append: function(el, o, returnElement){
552             return doInsert(el, o, returnElement, beforeend, '', true);
553         },
554
555             /**
556              * Creates new DOM element(s) without inserting them to the document.
557              * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
558              * @return {HTMLElement} The new uninserted node
559              */
560         createDom: createDom
561         };
562         return pub;
563 }());/**
564  * @class Ext.Template
565  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
566  * for greater performance.</p>
567  * <p>For example usage {@link #Template see the constructor}.</p>
568  * 
569  * @constructor
570  * An instance of this class may be created by passing to the constructor either
571  * a single argument, or multiple arguments:
572  * <div class="mdetail-params"><ul>
573  * <li><b>single argument</b> : String/Array
574  * <div class="sub-desc">
575  * The single argument may be either a String or an Array:<ul>
576  * <li><tt>String</tt> : </li><pre><code>
577 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
578 t.{@link #append}('some-element', ['foo']);
579  * </code></pre>
580  * <li><tt>Array</tt> : </li>
581  * An Array will be combined with <code>join('')</code>.
582 <pre><code>
583 var t = new Ext.Template([
584     '&lt;div name="{id}"&gt;',
585         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
586     '&lt;/div&gt;',
587 ]);
588 t.{@link #compile}();
589 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
590 </code></pre>
591  * </ul></div></li>
592  * <li><b>multiple arguments</b> : String, Object, Array, ...
593  * <div class="sub-desc">
594  * Multiple arguments will be combined with <code>join('')</code>.
595  * <pre><code>
596 var t = new Ext.Template(
597     '&lt;div name="{id}"&gt;',
598         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
599     '&lt;/div&gt;',
600     // a configuration object:
601     {
602         compiled: true,      // {@link #compile} immediately
603         disableFormats: true // See Notes below.
604     } 
605 );
606  * </code></pre>
607  * <p><b>Notes</b>:</p>
608  * <div class="mdetail-params"><ul>
609  * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
610  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
611  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
612  * when no formatting is required.</li>
613  * </ul></div>
614  * </div></li>
615  * </ul></div>
616  * @param {Mixed} config
617  */
618 Ext.Template = function(html){
619     var me = this,
620         a = arguments,
621         buf = [];
622
623     if (Ext.isArray(html)) {
624         html = html.join("");
625     } else if (a.length > 1) {
626             Ext.each(a, function(v) {
627             if (Ext.isObject(v)) {
628                 Ext.apply(me, v);
629             } else {
630                 buf.push(v);
631             }
632         });
633         html = buf.join('');
634     }
635
636     /**@private*/
637     me.html = html;
638     /**
639      * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
640      * immediately (see <code>{@link #compile}</code>).
641      * Defaults to <tt>false</tt>.
642      */
643     if (me.compiled) {
644         me.compile();
645     }
646 };
647 Ext.Template.prototype = {
648     /**
649      * @cfg {RegExp} re The regular expression used to match template variables.
650      * Defaults to:<pre><code>
651      * re : /\{([\w-]+)\}/g                                     // for Ext Core
652      * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g      // for Ext JS
653      * </code></pre>
654      */
655     re : /\{([\w-]+)\}/g,
656     /**
657      * See <code>{@link #re}</code>.
658      * @type RegExp
659      * @property re
660      */
661
662     /**
663      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
664      * @param {Object/Array} values
665      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
666      * or an object (i.e. <code>{foo: 'bar'}</code>).
667      * @return {String} The HTML fragment
668      */
669     applyTemplate : function(values){
670                 var me = this;
671
672         return me.compiled ?
673                         me.compiled(values) :
674                                 me.html.replace(me.re, function(m, name){
675                                 return values[name] !== undefined ? values[name] : "";
676                         });
677         },
678
679     /**
680      * Sets the HTML used as the template and optionally compiles it.
681      * @param {String} html
682      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
683      * @return {Ext.Template} this
684      */
685     set : function(html, compile){
686             var me = this;
687         me.html = html;
688         me.compiled = null;
689         return compile ? me.compile() : me;
690     },
691
692     /**
693      * Compiles the template into an internal function, eliminating the RegEx overhead.
694      * @return {Ext.Template} this
695      */
696     compile : function(){
697         var me = this,
698                 sep = Ext.isGecko ? "+" : ",";
699
700         function fn(m, name){                        
701                 name = "values['" + name + "']";
702                 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
703         }
704                 
705         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
706              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
707              (Ext.isGecko ?  "';};" : "'].join('');};"));
708         return me;
709     },
710
711     /**
712      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
713      * @param {Mixed} el The context element
714      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
715      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
716      * @return {HTMLElement/Ext.Element} The new node or Element
717      */
718     insertFirst: function(el, values, returnElement){
719         return this.doInsert('afterBegin', el, values, returnElement);
720     },
721
722     /**
723      * Applies the supplied values to the template and inserts the new node(s) before el.
724      * @param {Mixed} el The context element
725      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
726      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
727      * @return {HTMLElement/Ext.Element} The new node or Element
728      */
729     insertBefore: function(el, values, returnElement){
730         return this.doInsert('beforeBegin', el, values, returnElement);
731     },
732
733     /**
734      * Applies the supplied values to the template and inserts the new node(s) after el.
735      * @param {Mixed} el The context element
736      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
737      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
738      * @return {HTMLElement/Ext.Element} The new node or Element
739      */
740     insertAfter : function(el, values, returnElement){
741         return this.doInsert('afterEnd', el, values, returnElement);
742     },
743
744     /**
745      * Applies the supplied <code>values</code> to the template and appends
746      * the new node(s) to the specified <code>el</code>.
747      * <p>For example usage {@link #Template see the constructor}.</p>
748      * @param {Mixed} el The context element
749      * @param {Object/Array} values
750      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
751      * or an object (i.e. <code>{foo: 'bar'}</code>).
752      * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
753      * @return {HTMLElement/Ext.Element} The new node or Element
754      */
755     append : function(el, values, returnElement){
756         return this.doInsert('beforeEnd', el, values, returnElement);
757     },
758
759     doInsert : function(where, el, values, returnEl){
760         el = Ext.getDom(el);
761         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
762         return returnEl ? Ext.get(newNode, true) : newNode;
763     },
764
765     /**
766      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
767      * @param {Mixed} el The context element
768      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
769      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
770      * @return {HTMLElement/Ext.Element} The new node or Element
771      */
772     overwrite : function(el, values, returnElement){
773         el = Ext.getDom(el);
774         el.innerHTML = this.applyTemplate(values);
775         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
776     }
777 };
778 /**
779  * Alias for {@link #applyTemplate}
780  * Returns an HTML fragment of this template with the specified <code>values</code> applied.
781  * @param {Object/Array} values
782  * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
783  * or an object (i.e. <code>{foo: 'bar'}</code>).
784  * @return {String} The HTML fragment
785  * @member Ext.Template
786  * @method apply
787  */
788 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
789
790 /**
791  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
792  * @param {String/HTMLElement} el A DOM element or its id
793  * @param {Object} config A configuration object
794  * @return {Ext.Template} The created template
795  * @static
796  */
797 Ext.Template.from = function(el, config){
798     el = Ext.getDom(el);
799     return new Ext.Template(el.value || el.innerHTML, config || '');
800 };/**
801  * @class Ext.Template
802  */
803 Ext.apply(Ext.Template.prototype, {
804     /**
805      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
806      * functions in the template. If the template does not contain
807      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
808      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
809      * <pre><code>
810 var t = new Ext.Template(
811     '&lt;div name="{id}"&gt;',
812         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
813     '&lt;/div&gt;',
814     {
815         compiled: true,      // {@link #compile} immediately
816         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
817     }    
818 );
819      * </code></pre>
820      * For a list of available format functions, see {@link Ext.util.Format}.
821      */
822     disableFormats : false,                             
823     /**
824      * See <code>{@link #disableFormats}</code>.
825      * @type Boolean
826      * @property disableFormats
827      */
828
829     /**
830      * The regular expression used to match template variables
831      * @type RegExp
832      * @property
833      * @hide repeat doc
834      */
835     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
836
837     /**
838      * Returns an HTML fragment of this template with the specified values applied.
839      * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
840      * @return {String} The HTML fragment
841      * @hide repeat doc
842      */
843     applyTemplate : function(values){
844                 var me = this,
845                         useF = me.disableFormats !== true,
846                 fm = Ext.util.Format, 
847                 tpl = me;           
848             
849         if(me.compiled){
850             return me.compiled(values);
851         }
852         function fn(m, name, format, args){
853             if (format && useF) {
854                 if (format.substr(0, 5) == "this.") {
855                     return tpl.call(format.substr(5), values[name], values);
856                 } else {
857                     if (args) {
858                         // quoted values are required for strings in compiled templates,
859                         // but for non compiled we need to strip them
860                         // quoted reversed for jsmin
861                         var re = /^\s*['"](.*)["']\s*$/;
862                         args = args.split(',');
863                         for(var i = 0, len = args.length; i < len; i++){
864                             args[i] = args[i].replace(re, "$1");
865                         }
866                         args = [values[name]].concat(args);
867                     } else {
868                         args = [values[name]];
869                     }
870                     return fm[format].apply(fm, args);
871                 }
872             } else {
873                 return values[name] !== undefined ? values[name] : "";
874             }
875         }
876         return me.html.replace(me.re, fn);
877     },
878                 
879     /**
880      * Compiles the template into an internal function, eliminating the RegEx overhead.
881      * @return {Ext.Template} this
882      * @hide repeat doc
883      */
884     compile : function(){
885         var me = this,
886                 fm = Ext.util.Format,
887                 useF = me.disableFormats !== true,
888                 sep = Ext.isGecko ? "+" : ",",
889                 body;
890         
891         function fn(m, name, format, args){
892             if(format && useF){
893                 args = args ? ',' + args : "";
894                 if(format.substr(0, 5) != "this."){
895                     format = "fm." + format + '(';
896                 }else{
897                     format = 'this.call("'+ format.substr(5) + '", ';
898                     args = ", values";
899                 }
900             }else{
901                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
902             }
903             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
904         }
905         
906         // branched to use + in gecko and [].join() in others
907         if(Ext.isGecko){
908             body = "this.compiled = function(values){ return '" +
909                    me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
910                     "';};";
911         }else{
912             body = ["this.compiled = function(values){ return ['"];
913             body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
914             body.push("'].join('');};");
915             body = body.join('');
916         }
917         eval(body);
918         return me;
919     },
920     
921     // private function used to call members
922     call : function(fnName, value, allValues){
923         return this[fnName](value, allValues);
924     }
925 });
926 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*
927  * This is code is also distributed under MIT license for use
928  * with jQuery and prototype JavaScript libraries.
929  */
930 /**
931  * @class Ext.DomQuery
932 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
933 <p>
934 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
935
936 <p>
937 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
938 </p>
939 <h4>Element Selectors:</h4>
940 <ul class="list">
941     <li> <b>*</b> any element</li>
942     <li> <b>E</b> an element with the tag E</li>
943     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
944     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
945     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
946     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
947 </ul>
948 <h4>Attribute Selectors:</h4>
949 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
950 <ul class="list">
951     <li> <b>E[foo]</b> has an attribute "foo"</li>
952     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
953     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
954     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
955     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
956     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
957     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
958 </ul>
959 <h4>Pseudo Classes:</h4>
960 <ul class="list">
961     <li> <b>E:first-child</b> E is the first child of its parent</li>
962     <li> <b>E:last-child</b> E is the last child of its parent</li>
963     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
964     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
965     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
966     <li> <b>E:only-child</b> E is the only child of its parent</li>
967     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
968     <li> <b>E:first</b> the first E in the resultset</li>
969     <li> <b>E:last</b> the last E in the resultset</li>
970     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
971     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
972     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
973     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
974     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
975     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
976     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
977     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
978     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
979     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
980 </ul>
981 <h4>CSS Value Selectors:</h4>
982 <ul class="list">
983     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
984     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
985     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
986     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
987     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
988     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
989 </ul>
990  * @singleton
991  */
992 Ext.DomQuery = function(){
993     var cache = {}, 
994         simpleCache = {}, 
995         valueCache = {},
996         nonSpace = /\S/,
997         trimRe = /^\s+|\s+$/g,
998         tplRe = /\{(\d+)\}/g,
999         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1000         tagTokenRe = /^(#)?([\w-\*]+)/,
1001         nthRe = /(\d*)n\+?(\d*)/, 
1002         nthRe2 = /\D/,
1003         // This is for IE MSXML which does not support expandos.
1004         // IE runs the same speed using setAttribute, however FF slows way down
1005         // and Safari completely fails so they need to continue to use expandos.
1006         isIE = window.ActiveXObject ? true : false,
1007         key = 30803;
1008     
1009     // this eval is stop the compressor from
1010     // renaming the variable to something shorter
1011     eval("var batch = 30803;");         
1012
1013     // Retrieve the child node from a particular
1014     // parent at the specified index.
1015     function child(parent, index){
1016         var i = 0,
1017             n = parent.firstChild;
1018         while(n){
1019             if(n.nodeType == 1){
1020                if(++i == index){
1021                    return n;
1022                }
1023             }
1024             n = n.nextSibling;
1025         }
1026         return null;
1027     }
1028
1029     // retrieve the next element node
1030     function next(n){   
1031         while((n = n.nextSibling) && n.nodeType != 1);
1032         return n;
1033     }
1034
1035     // retrieve the previous element node 
1036     function prev(n){
1037         while((n = n.previousSibling) && n.nodeType != 1);
1038         return n;
1039     }
1040
1041     // Mark each child node with a nodeIndex skipping and
1042     // removing empty text nodes.
1043     function children(parent){
1044         var n = parent.firstChild,
1045             nodeIndex = -1,
1046             nextNode;
1047         while(n){
1048             nextNode = n.nextSibling;
1049             // clean worthless empty nodes.
1050             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1051                 parent.removeChild(n);
1052             }else{
1053                 // add an expando nodeIndex
1054                 n.nodeIndex = ++nodeIndex;
1055             }
1056             n = nextNode;
1057         }
1058         return this;
1059     }
1060
1061
1062     // nodeSet - array of nodes
1063     // cls - CSS Class
1064     function byClassName(nodeSet, cls){
1065         if(!cls){
1066             return nodeSet;
1067         }
1068         var result = [], ri = -1;
1069         for(var i = 0, ci; ci = nodeSet[i]; i++){
1070             if((' '+ci.className+' ').indexOf(cls) != -1){
1071                 result[++ri] = ci;
1072             }
1073         }
1074         return result;
1075     };
1076
1077     function attrValue(n, attr){
1078         // if its an array, use the first node.
1079         if(!n.tagName && typeof n.length != "undefined"){
1080             n = n[0];
1081         }
1082         if(!n){
1083             return null;
1084         }
1085
1086         if(attr == "for"){
1087             return n.htmlFor;
1088         }
1089         if(attr == "class" || attr == "className"){
1090             return n.className;
1091         }
1092         return n.getAttribute(attr) || n[attr];
1093
1094     };
1095
1096
1097     // ns - nodes
1098     // mode - false, /, >, +, ~
1099     // tagName - defaults to "*"
1100     function getNodes(ns, mode, tagName){
1101         var result = [], ri = -1, cs;
1102         if(!ns){
1103             return result;
1104         }
1105         tagName = tagName || "*";
1106         // convert to array
1107         if(typeof ns.getElementsByTagName != "undefined"){
1108             ns = [ns];
1109         }
1110         
1111         // no mode specified, grab all elements by tagName
1112         // at any depth
1113         if(!mode){
1114             for(var i = 0, ni; ni = ns[i]; i++){
1115                 cs = ni.getElementsByTagName(tagName);
1116                 for(var j = 0, ci; ci = cs[j]; j++){
1117                     result[++ri] = ci;
1118                 }
1119             }
1120         // Direct Child mode (/ or >)
1121         // E > F or E/F all direct children elements of E that have the tag     
1122         } else if(mode == "/" || mode == ">"){
1123             var utag = tagName.toUpperCase();
1124             for(var i = 0, ni, cn; ni = ns[i]; i++){
1125                 cn = ni.childNodes;
1126                 for(var j = 0, cj; cj = cn[j]; j++){
1127                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
1128                         result[++ri] = cj;
1129                     }
1130                 }
1131             }
1132         // Immediately Preceding mode (+)
1133         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1134         }else if(mode == "+"){
1135             var utag = tagName.toUpperCase();
1136             for(var i = 0, n; n = ns[i]; i++){
1137                 while((n = n.nextSibling) && n.nodeType != 1);
1138                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1139                     result[++ri] = n;
1140                 }
1141             }
1142         // Sibling mode (~)
1143         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1144         }else if(mode == "~"){
1145             var utag = tagName.toUpperCase();
1146             for(var i = 0, n; n = ns[i]; i++){
1147                 while((n = n.nextSibling)){
1148                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1149                         result[++ri] = n;
1150                     }
1151                 }
1152             }
1153         }
1154         return result;
1155     }
1156
1157     function concat(a, b){
1158         if(b.slice){
1159             return a.concat(b);
1160         }
1161         for(var i = 0, l = b.length; i < l; i++){
1162             a[a.length] = b[i];
1163         }
1164         return a;
1165     }
1166
1167     function byTag(cs, tagName){
1168         if(cs.tagName || cs == document){
1169             cs = [cs];
1170         }
1171         if(!tagName){
1172             return cs;
1173         }
1174         var result = [], ri = -1;
1175         tagName = tagName.toLowerCase();
1176         for(var i = 0, ci; ci = cs[i]; i++){
1177             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1178                 result[++ri] = ci;
1179             }
1180         }
1181         return result;
1182     }
1183
1184     function byId(cs, id){
1185         if(cs.tagName || cs == document){
1186             cs = [cs];
1187         }
1188         if(!id){
1189             return cs;
1190         }
1191         var result = [], ri = -1;
1192         for(var i = 0, ci; ci = cs[i]; i++){
1193             if(ci && ci.id == id){
1194                 result[++ri] = ci;
1195                 return result;
1196             }
1197         }
1198         return result;
1199     }
1200
1201     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1202     // custom can be "{"
1203     function byAttribute(cs, attr, value, op, custom){
1204         var result = [], 
1205             ri = -1, 
1206             useGetStyle = custom == "{",            
1207             fn = Ext.DomQuery.operators[op],        
1208             a,      
1209             innerHTML;
1210         for(var i = 0, ci; ci = cs[i]; i++){
1211             // skip non-element nodes.
1212             if(ci.nodeType != 1){
1213                 continue;
1214             }
1215             
1216             innerHTML = ci.innerHTML;
1217             // we only need to change the property names if we're dealing with html nodes, not XML
1218             if(innerHTML !== null && innerHTML !== undefined){
1219                 if(useGetStyle){
1220                     a = Ext.DomQuery.getStyle(ci, attr);
1221                 } else if (attr == "class" || attr == "className"){
1222                     a = ci.className;
1223                 } else if (attr == "for"){
1224                     a = ci.htmlFor;
1225                 } else if (attr == "href"){
1226                     // getAttribute href bug
1227                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1228                     a = ci.getAttribute("href", 2);
1229                 } else{
1230                     a = ci.getAttribute(attr);
1231                 }
1232             }else{
1233                 a = ci.getAttribute(attr);
1234             }
1235             if((fn && fn(a, value)) || (!fn && a)){
1236                 result[++ri] = ci;
1237             }
1238         }
1239         return result;
1240     }
1241
1242     function byPseudo(cs, name, value){
1243         return Ext.DomQuery.pseudos[name](cs, value);
1244     }
1245
1246     function nodupIEXml(cs){
1247         var d = ++key, 
1248             r;
1249         cs[0].setAttribute("_nodup", d);
1250         r = [cs[0]];
1251         for(var i = 1, len = cs.length; i < len; i++){
1252             var c = cs[i];
1253             if(!c.getAttribute("_nodup") != d){
1254                 c.setAttribute("_nodup", d);
1255                 r[r.length] = c;
1256             }
1257         }
1258         for(var i = 0, len = cs.length; i < len; i++){
1259             cs[i].removeAttribute("_nodup");
1260         }
1261         return r;
1262     }
1263
1264     function nodup(cs){
1265         if(!cs){
1266             return [];
1267         }
1268         var len = cs.length, c, i, r = cs, cj, ri = -1;
1269         if(!len || typeof cs.nodeType != "undefined" || len == 1){
1270             return cs;
1271         }
1272         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1273             return nodupIEXml(cs);
1274         }
1275         var d = ++key;
1276         cs[0]._nodup = d;
1277         for(i = 1; c = cs[i]; i++){
1278             if(c._nodup != d){
1279                 c._nodup = d;
1280             }else{
1281                 r = [];
1282                 for(var j = 0; j < i; j++){
1283                     r[++ri] = cs[j];
1284                 }
1285                 for(j = i+1; cj = cs[j]; j++){
1286                     if(cj._nodup != d){
1287                         cj._nodup = d;
1288                         r[++ri] = cj;
1289                     }
1290                 }
1291                 return r;
1292             }
1293         }
1294         return r;
1295     }
1296
1297     function quickDiffIEXml(c1, c2){
1298         var d = ++key,
1299             r = [];
1300         for(var i = 0, len = c1.length; i < len; i++){
1301             c1[i].setAttribute("_qdiff", d);
1302         }        
1303         for(var i = 0, len = c2.length; i < len; i++){
1304             if(c2[i].getAttribute("_qdiff") != d){
1305                 r[r.length] = c2[i];
1306             }
1307         }
1308         for(var i = 0, len = c1.length; i < len; i++){
1309            c1[i].removeAttribute("_qdiff");
1310         }
1311         return r;
1312     }
1313
1314     function quickDiff(c1, c2){
1315         var len1 = c1.length,
1316                 d = ++key,
1317                 r = [];
1318         if(!len1){
1319             return c2;
1320         }
1321         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1322             return quickDiffIEXml(c1, c2);
1323         }        
1324         for(var i = 0; i < len1; i++){
1325             c1[i]._qdiff = d;
1326         }        
1327         for(var i = 0, len = c2.length; i < len; i++){
1328             if(c2[i]._qdiff != d){
1329                 r[r.length] = c2[i];
1330             }
1331         }
1332         return r;
1333     }
1334
1335     function quickId(ns, mode, root, id){
1336         if(ns == root){
1337            var d = root.ownerDocument || root;
1338            return d.getElementById(id);
1339         }
1340         ns = getNodes(ns, mode, "*");
1341         return byId(ns, id);
1342     }
1343
1344     return {
1345         getStyle : function(el, name){
1346             return Ext.fly(el).getStyle(name);
1347         },
1348         /**
1349          * Compiles a selector/xpath query into a reusable function. The returned function
1350          * takes one parameter "root" (optional), which is the context node from where the query should start.
1351          * @param {String} selector The selector/xpath query
1352          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1353          * @return {Function}
1354          */
1355         compile : function(path, type){
1356             type = type || "select";
1357
1358             // setup fn preamble
1359             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1360                 mode,           
1361                 lastPath,
1362                 matchers = Ext.DomQuery.matchers,
1363                 matchersLn = matchers.length,
1364                 modeMatch,
1365                 // accept leading mode switch
1366                 lmode = path.match(modeRe);
1367             
1368             if(lmode && lmode[1]){
1369                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1370                 path = path.replace(lmode[1], "");
1371             }
1372             
1373             // strip leading slashes
1374             while(path.substr(0, 1)=="/"){
1375                 path = path.substr(1);
1376             }
1377
1378             while(path && lastPath != path){
1379                 lastPath = path;
1380                 var tokenMatch = path.match(tagTokenRe);
1381                 if(type == "select"){
1382                     if(tokenMatch){
1383                         // ID Selector
1384                         if(tokenMatch[1] == "#"){
1385                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                 
1386                         }else{
1387                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1388                         }
1389                         path = path.replace(tokenMatch[0], "");
1390                     }else if(path.substr(0, 1) != '@'){
1391                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
1392                     }
1393                 // type of "simple"
1394                 }else{
1395                     if(tokenMatch){
1396                         if(tokenMatch[1] == "#"){
1397                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1398                         }else{
1399                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1400                         }
1401                         path = path.replace(tokenMatch[0], "");
1402                     }
1403                 }
1404                 while(!(modeMatch = path.match(modeRe))){
1405                     var matched = false;
1406                     for(var j = 0; j < matchersLn; j++){
1407                         var t = matchers[j];
1408                         var m = path.match(t.re);
1409                         if(m){
1410                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
1411                                 return m[i];
1412                             });
1413                             path = path.replace(m[0], "");
1414                             matched = true;
1415                             break;
1416                         }
1417                     }
1418                     // prevent infinite loop on bad selector
1419                     if(!matched){
1420                         throw 'Error parsing selector, parsing failed at "' + path + '"';
1421                     }
1422                 }
1423                 if(modeMatch[1]){
1424                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1425                     path = path.replace(modeMatch[1], "");
1426                 }
1427             }
1428             // close fn out
1429             fn[fn.length] = "return nodup(n);\n}";
1430             
1431             // eval fn and return it
1432             eval(fn.join(""));
1433             return f;
1434         },
1435
1436         /**
1437          * Selects a group of elements.
1438          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1439          * @param {Node/String} root (optional) The start of the query (defaults to document).
1440          * @return {Array} An Array of DOM elements which match the selector. If there are
1441          * no matches, and empty Array is returned.
1442          */
1443         jsSelect: function(path, root, type){
1444             // set root to doc if not specified.
1445             root = root || document;
1446             
1447             if(typeof root == "string"){
1448                 root = document.getElementById(root);
1449             }
1450             var paths = path.split(","),
1451                 results = [];
1452                 
1453             // loop over each selector
1454             for(var i = 0, len = paths.length; i < len; i++){           
1455                 var subPath = paths[i].replace(trimRe, "");
1456                 // compile and place in cache
1457                 if(!cache[subPath]){
1458                     cache[subPath] = Ext.DomQuery.compile(subPath);
1459                     if(!cache[subPath]){
1460                         throw subPath + " is not a valid selector";
1461                     }
1462                 }
1463                 var result = cache[subPath](root);
1464                 if(result && result != document){
1465                     results = results.concat(result);
1466                 }
1467             }
1468             
1469             // if there were multiple selectors, make sure dups
1470             // are eliminated
1471             if(paths.length > 1){
1472                 return nodup(results);
1473             }
1474             return results;
1475         },
1476         isXml: function(el) {
1477             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1478             return docEl ? docEl.nodeName !== "HTML" : false;
1479         },
1480         select : document.querySelectorAll ? function(path, root, type) {
1481             root = root || document;
1482             if (!Ext.DomQuery.isXml(root)) {
1483                 try {
1484                     var cs = root.querySelectorAll(path);
1485                     return Ext.toArray(cs);
1486                 }
1487                 catch (ex) {}           
1488             }       
1489             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1490         } : function(path, root, type) {
1491             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1492         },
1493
1494         /**
1495          * Selects a single element.
1496          * @param {String} selector The selector/xpath query
1497          * @param {Node} root (optional) The start of the query (defaults to document).
1498          * @return {Element} The DOM element which matched the selector.
1499          */
1500         selectNode : function(path, root){
1501             return Ext.DomQuery.select(path, root)[0];
1502         },
1503
1504         /**
1505          * Selects the value of a node, optionally replacing null with the defaultValue.
1506          * @param {String} selector The selector/xpath query
1507          * @param {Node} root (optional) The start of the query (defaults to document).
1508          * @param {String} defaultValue
1509          * @return {String}
1510          */
1511         selectValue : function(path, root, defaultValue){
1512             path = path.replace(trimRe, "");
1513             if(!valueCache[path]){
1514                 valueCache[path] = Ext.DomQuery.compile(path, "select");
1515             }
1516             var n = valueCache[path](root), v;
1517             n = n[0] ? n[0] : n;
1518                     
1519             // overcome a limitation of maximum textnode size
1520             // Rumored to potentially crash IE6 but has not been confirmed.
1521             // http://reference.sitepoint.com/javascript/Node/normalize
1522             // https://developer.mozilla.org/En/DOM/Node.normalize          
1523             if (typeof n.normalize == 'function') n.normalize();
1524             
1525             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1526             return ((v === null||v === undefined||v==='') ? defaultValue : v);
1527         },
1528
1529         /**
1530          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1531          * @param {String} selector The selector/xpath query
1532          * @param {Node} root (optional) The start of the query (defaults to document).
1533          * @param {Number} defaultValue
1534          * @return {Number}
1535          */
1536         selectNumber : function(path, root, defaultValue){
1537             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1538             return parseFloat(v);
1539         },
1540
1541         /**
1542          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1543          * @param {String/HTMLElement/Array} el An element id, element or array of elements
1544          * @param {String} selector The simple selector to test
1545          * @return {Boolean}
1546          */
1547         is : function(el, ss){
1548             if(typeof el == "string"){
1549                 el = document.getElementById(el);
1550             }
1551             var isArray = Ext.isArray(el),
1552                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1553             return isArray ? (result.length == el.length) : (result.length > 0);
1554         },
1555
1556         /**
1557          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1558          * @param {Array} el An array of elements to filter
1559          * @param {String} selector The simple selector to test
1560          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1561          * the selector instead of the ones that match
1562          * @return {Array} An Array of DOM elements which match the selector. If there are
1563          * no matches, and empty Array is returned.
1564          */
1565         filter : function(els, ss, nonMatches){
1566             ss = ss.replace(trimRe, "");
1567             if(!simpleCache[ss]){
1568                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1569             }
1570             var result = simpleCache[ss](els);
1571             return nonMatches ? quickDiff(result, els) : result;
1572         },
1573
1574         /**
1575          * Collection of matching regular expressions and code snippets.
1576          * Each capture group within () will be replace the {} in the select
1577          * statement as specified by their index.
1578          */
1579         matchers : [{
1580                 re: /^\.([\w-]+)/,
1581                 select: 'n = byClassName(n, " {1} ");'
1582             }, {
1583                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1584                 select: 'n = byPseudo(n, "{1}", "{2}");'
1585             },{
1586                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1587                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1588             }, {
1589                 re: /^#([\w-]+)/,
1590                 select: 'n = byId(n, "{1}");'
1591             },{
1592                 re: /^@([\w-]+)/,
1593                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1594             }
1595         ],
1596
1597         /**
1598          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1599          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
1600          */
1601         operators : {
1602             "=" : function(a, v){
1603                 return a == v;
1604             },
1605             "!=" : function(a, v){
1606                 return a != v;
1607             },
1608             "^=" : function(a, v){
1609                 return a && a.substr(0, v.length) == v;
1610             },
1611             "$=" : function(a, v){
1612                 return a && a.substr(a.length-v.length) == v;
1613             },
1614             "*=" : function(a, v){
1615                 return a && a.indexOf(v) !== -1;
1616             },
1617             "%=" : function(a, v){
1618                 return (a % v) == 0;
1619             },
1620             "|=" : function(a, v){
1621                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1622             },
1623             "~=" : function(a, v){
1624                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1625             }
1626         },
1627
1628         /**
1629          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1630          * two parameters:</p><div class="mdetail-params"><ul>
1631          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1632          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1633          * </ul></div>
1634          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1635          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1636          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1637          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>
1638          * <code><pre>
1639 Ext.DomQuery.pseudos.external = function(c, v){
1640     var r = [], ri = -1;
1641     for(var i = 0, ci; ci = c[i]; i++){
1642 //      Include in result set only if it's a link to an external resource
1643         if(ci.hostname != location.hostname){
1644             r[++ri] = ci;
1645         }
1646     }
1647     return r;
1648 };</pre></code>
1649          * Then external links could be gathered with the following statement:<code><pre>
1650 var externalLinks = Ext.select("a:external");
1651 </code></pre>
1652          */
1653         pseudos : {
1654             "first-child" : function(c){
1655                 var r = [], ri = -1, n;
1656                 for(var i = 0, ci; ci = n = c[i]; i++){
1657                     while((n = n.previousSibling) && n.nodeType != 1);
1658                     if(!n){
1659                         r[++ri] = ci;
1660                     }
1661                 }
1662                 return r;
1663             },
1664
1665             "last-child" : function(c){
1666                 var r = [], ri = -1, n;
1667                 for(var i = 0, ci; ci = n = c[i]; i++){
1668                     while((n = n.nextSibling) && n.nodeType != 1);
1669                     if(!n){
1670                         r[++ri] = ci;
1671                     }
1672                 }
1673                 return r;
1674             },
1675
1676             "nth-child" : function(c, a) {
1677                 var r = [], ri = -1,
1678                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1679                         f = (m[1] || 1) - 0, l = m[2] - 0;
1680                 for(var i = 0, n; n = c[i]; i++){
1681                     var pn = n.parentNode;
1682                     if (batch != pn._batch) {
1683                         var j = 0;
1684                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1685                             if(cn.nodeType == 1){
1686                                cn.nodeIndex = ++j;
1687                             }
1688                         }
1689                         pn._batch = batch;
1690                     }
1691                     if (f == 1) {
1692                         if (l == 0 || n.nodeIndex == l){
1693                             r[++ri] = n;
1694                         }
1695                     } else if ((n.nodeIndex + l) % f == 0){
1696                         r[++ri] = n;
1697                     }
1698                 }
1699
1700                 return r;
1701             },
1702
1703             "only-child" : function(c){
1704                 var r = [], ri = -1;;
1705                 for(var i = 0, ci; ci = c[i]; i++){
1706                     if(!prev(ci) && !next(ci)){
1707                         r[++ri] = ci;
1708                     }
1709                 }
1710                 return r;
1711             },
1712
1713             "empty" : function(c){
1714                 var r = [], ri = -1;
1715                 for(var i = 0, ci; ci = c[i]; i++){
1716                     var cns = ci.childNodes, j = 0, cn, empty = true;
1717                     while(cn = cns[j]){
1718                         ++j;
1719                         if(cn.nodeType == 1 || cn.nodeType == 3){
1720                             empty = false;
1721                             break;
1722                         }
1723                     }
1724                     if(empty){
1725                         r[++ri] = ci;
1726                     }
1727                 }
1728                 return r;
1729             },
1730
1731             "contains" : function(c, v){
1732                 var r = [], ri = -1;
1733                 for(var i = 0, ci; ci = c[i]; i++){
1734                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1735                         r[++ri] = ci;
1736                     }
1737                 }
1738                 return r;
1739             },
1740
1741             "nodeValue" : function(c, v){
1742                 var r = [], ri = -1;
1743                 for(var i = 0, ci; ci = c[i]; i++){
1744                     if(ci.firstChild && ci.firstChild.nodeValue == v){
1745                         r[++ri] = ci;
1746                     }
1747                 }
1748                 return r;
1749             },
1750
1751             "checked" : function(c){
1752                 var r = [], ri = -1;
1753                 for(var i = 0, ci; ci = c[i]; i++){
1754                     if(ci.checked == true){
1755                         r[++ri] = ci;
1756                     }
1757                 }
1758                 return r;
1759             },
1760
1761             "not" : function(c, ss){
1762                 return Ext.DomQuery.filter(c, ss, true);
1763             },
1764
1765             "any" : function(c, selectors){
1766                 var ss = selectors.split('|'),
1767                         r = [], ri = -1, s;
1768                 for(var i = 0, ci; ci = c[i]; i++){
1769                     for(var j = 0; s = ss[j]; j++){
1770                         if(Ext.DomQuery.is(ci, s)){
1771                             r[++ri] = ci;
1772                             break;
1773                         }
1774                     }
1775                 }
1776                 return r;
1777             },
1778
1779             "odd" : function(c){
1780                 return this["nth-child"](c, "odd");
1781             },
1782
1783             "even" : function(c){
1784                 return this["nth-child"](c, "even");
1785             },
1786
1787             "nth" : function(c, a){
1788                 return c[a-1] || [];
1789             },
1790
1791             "first" : function(c){
1792                 return c[0] || [];
1793             },
1794
1795             "last" : function(c){
1796                 return c[c.length-1] || [];
1797             },
1798
1799             "has" : function(c, ss){
1800                 var s = Ext.DomQuery.select,
1801                         r = [], ri = -1;
1802                 for(var i = 0, ci; ci = c[i]; i++){
1803                     if(s(ss, ci).length > 0){
1804                         r[++ri] = ci;
1805                     }
1806                 }
1807                 return r;
1808             },
1809
1810             "next" : function(c, ss){
1811                 var is = Ext.DomQuery.is,
1812                         r = [], ri = -1;
1813                 for(var i = 0, ci; ci = c[i]; i++){
1814                     var n = next(ci);
1815                     if(n && is(n, ss)){
1816                         r[++ri] = ci;
1817                     }
1818                 }
1819                 return r;
1820             },
1821
1822             "prev" : function(c, ss){
1823                 var is = Ext.DomQuery.is,
1824                         r = [], ri = -1;
1825                 for(var i = 0, ci; ci = c[i]; i++){
1826                     var n = prev(ci);
1827                     if(n && is(n, ss)){
1828                         r[++ri] = ci;
1829                     }
1830                 }
1831                 return r;
1832             }
1833         }
1834     };
1835 }();
1836
1837 /**
1838  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1839  * @param {String} path The selector/xpath query
1840  * @param {Node} root (optional) The start of the query (defaults to document).
1841  * @return {Array}
1842  * @member Ext
1843  * @method query
1844  */
1845 Ext.query = Ext.DomQuery.select;
1846 /**
1847  * @class Ext.util.DelayedTask
1848  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1849  * performing setTimeout where a new timeout cancels the old timeout. When called, the
1850  * task will wait the specified time period before executing. If durng that time period,
1851  * the task is called again, the original call will be cancelled. This continues so that
1852  * the function is only called a single time for each iteration.</p>
1853  * <p>This method is especially useful for things like detecting whether a user has finished
1854  * typing in a text field. An example would be performing validation on a keypress. You can
1855  * use this class to buffer the keypress events for a certain number of milliseconds, and
1856  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
1857 var task = new Ext.util.DelayedTask(function(){
1858     alert(Ext.getDom('myInputField').value.length);
1859 });
1860 // Wait 500ms before calling our function. If the user presses another key 
1861 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1862 Ext.get('myInputField').on('keypress', function(){
1863     task.{@link #delay}(500); 
1864 });
1865  * </code></pre> 
1866  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1867  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1868  * also setup a delayed task for you to buffer events.</p> 
1869  * @constructor The parameters to this constructor serve as defaults and are not required.
1870  * @param {Function} fn (optional) The default function to call.
1871  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1872  * function is called. If not specified, <code>this</code> will refer to the browser window.
1873  * @param {Array} args (optional) The default Array of arguments.
1874  */
1875 Ext.util.DelayedTask = function(fn, scope, args){
1876     var me = this,
1877         id,     
1878         call = function(){
1879                 clearInterval(id);
1880                 id = null;
1881                 fn.apply(scope, args || []);
1882             };
1883             
1884     /**
1885      * Cancels any pending timeout and queues a new one
1886      * @param {Number} delay The milliseconds to delay
1887      * @param {Function} newFn (optional) Overrides function passed to constructor
1888      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1889      * is specified, <code>this</code> will refer to the browser window.
1890      * @param {Array} newArgs (optional) Overrides args passed to constructor
1891      */
1892     me.delay = function(delay, newFn, newScope, newArgs){
1893         me.cancel();
1894         fn = newFn || fn;
1895         scope = newScope || scope;
1896         args = newArgs || args;
1897         id = setInterval(call, delay);
1898     };
1899
1900     /**
1901      * Cancel the last queued timeout
1902      */
1903     me.cancel = function(){
1904         if(id){
1905             clearInterval(id);
1906             id = null;
1907         }
1908     };
1909 };(function(){
1910
1911 var EXTUTIL = Ext.util,
1912     TOARRAY = Ext.toArray,
1913     EACH = Ext.each,
1914     ISOBJECT = Ext.isObject,
1915     TRUE = true,
1916     FALSE = false;
1917 /**
1918  * @class Ext.util.Observable
1919  * Base class that provides a common interface for publishing events. Subclasses are expected to
1920  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1921  * with configured listeners defined.<br>
1922  * For example:
1923  * <pre><code>
1924 Employee = Ext.extend(Ext.util.Observable, {
1925     constructor: function(config){
1926         this.name = config.name;
1927         this.addEvents({
1928             "fired" : true,
1929             "quit" : true
1930         });
1931
1932         // Copy configured listeners into *this* object so that the base class&#39;s
1933         // constructor will add them.
1934         this.listeners = config.listeners;
1935
1936         // Call our superclass constructor to complete construction process.
1937         Employee.superclass.constructor.call(this, config)
1938     }
1939 });
1940 </code></pre>
1941  * This could then be used like this:<pre><code>
1942 var newEmployee = new Employee({
1943     name: employeeName,
1944     listeners: {
1945         quit: function() {
1946             // By default, "this" will be the object that fired the event.
1947             alert(this.name + " has quit!");
1948         }
1949     }
1950 });
1951 </code></pre>
1952  */
1953 EXTUTIL.Observable = function(){
1954     /**
1955      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1956      * object during initialization.  This should be a valid listeners config object as specified in the
1957      * {@link #addListener} example for attaching multiple handlers at once.</p>
1958      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1959      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1960      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1961      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1962      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1963      * has been rendered. A plugin can simplify this step:<pre><code>
1964 // Plugin is configured with a listeners config object.
1965 // The Component is appended to the argument list of all handler functions.
1966 Ext.DomObserver = Ext.extend(Object, {
1967     constructor: function(config) {
1968         this.listeners = config.listeners ? config.listeners : config;
1969     },
1970
1971     // Component passes itself into plugin&#39;s init method
1972     init: function(c) {
1973         var p, l = this.listeners;
1974         for (p in l) {
1975             if (Ext.isFunction(l[p])) {
1976                 l[p] = this.createHandler(l[p], c);
1977             } else {
1978                 l[p].fn = this.createHandler(l[p].fn, c);
1979             }
1980         }
1981
1982         // Add the listeners to the Element immediately following the render call
1983         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1984             var e = c.getEl();
1985             if (e) {
1986                 e.on(l);
1987             }
1988         });
1989     },
1990
1991     createHandler: function(fn, c) {
1992         return function(e) {
1993             fn.call(this, e, c);
1994         };
1995     }
1996 });
1997
1998 var combo = new Ext.form.ComboBox({
1999
2000     // Collapse combo when its element is clicked on
2001     plugins: [ new Ext.DomObserver({
2002         click: function(evt, comp) {
2003             comp.collapse();
2004         }
2005     })],
2006     store: myStore,
2007     typeAhead: true,
2008     mode: 'local',
2009     triggerAction: 'all'
2010 });
2011      * </code></pre></p>
2012      */
2013     var me = this, e = me.events;
2014     if(me.listeners){
2015         me.on(me.listeners);
2016         delete me.listeners;
2017     }
2018     me.events = e || {};
2019 };
2020
2021 EXTUTIL.Observable.prototype = {
2022     // private
2023     filterOptRe : /^(?:scope|delay|buffer|single)$/,
2024
2025     /**
2026      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2027      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2028      * by calling {@link #enableBubble}.</p>
2029      * @param {String} eventName The name of the event to fire.
2030      * @param {Object...} args Variable number of parameters are passed to handlers.
2031      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2032      */
2033     fireEvent : function(){
2034         var a = TOARRAY(arguments),
2035             ename = a[0].toLowerCase(),
2036             me = this,
2037             ret = TRUE,
2038             ce = me.events[ename],
2039             q,
2040             c;
2041         if (me.eventsSuspended === TRUE) {
2042             if (q = me.eventQueue) {
2043                 q.push(a);
2044             }
2045         }
2046         else if(ISOBJECT(ce) && ce.bubble){
2047             if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2048                 return FALSE;
2049             }
2050             c = me.getBubbleTarget && me.getBubbleTarget();
2051             if(c && c.enableBubble) {
2052                 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
2053                     c.enableBubble(ename);
2054                 }
2055                 return c.fireEvent.apply(c, a);
2056             }
2057         }
2058         else {
2059             if (ISOBJECT(ce)) {
2060                 a.shift();
2061                 ret = ce.fire.apply(ce, a);
2062             }
2063         }
2064         return ret;
2065     },
2066
2067     /**
2068      * Appends an event handler to this object.
2069      * @param {String}   eventName The name of the event to listen for.
2070      * @param {Function} handler The method the event invokes.
2071      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2072      * <b>If omitted, defaults to the object which fired the event.</b>
2073      * @param {Object}   options (optional) An object containing handler configuration.
2074      * properties. This may contain any of the following properties:<ul>
2075      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2076      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2077      * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
2078      * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
2079      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2080      * by the specified number of milliseconds. If the event fires again within that time, the original
2081      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2082      * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
2083      * if the event was bubbled up from a child Observable.</div></li>
2084      * </ul><br>
2085      * <p>
2086      * <b>Combining Options</b><br>
2087      * Using the options argument, it is possible to combine different types of listeners:<br>
2088      * <br>
2089      * A delayed, one-time listener.
2090      * <pre><code>
2091 myDataView.on('click', this.onClick, this, {
2092 single: true,
2093 delay: 100
2094 });</code></pre>
2095      * <p>
2096      * <b>Attaching multiple handlers in 1 call</b><br>
2097      * The method also allows for a single argument to be passed which is a config object containing properties
2098      * which specify multiple handlers.
2099      * <p>
2100      * <pre><code>
2101 myGridPanel.on({
2102 'click' : {
2103     fn: this.onClick,
2104     scope: this,
2105     delay: 100
2106 },
2107 'mouseover' : {
2108     fn: this.onMouseOver,
2109     scope: this
2110 },
2111 'mouseout' : {
2112     fn: this.onMouseOut,
2113     scope: this
2114 }
2115 });</code></pre>
2116  * <p>
2117  * Or a shorthand syntax:<br>
2118  * <pre><code>
2119 myGridPanel.on({
2120 'click' : this.onClick,
2121 'mouseover' : this.onMouseOver,
2122 'mouseout' : this.onMouseOut,
2123  scope: this
2124 });</code></pre>
2125      */
2126     addListener : function(eventName, fn, scope, o){
2127         var me = this,
2128             e,
2129             oe,
2130             isF,
2131         ce;
2132         if (ISOBJECT(eventName)) {
2133             o = eventName;
2134             for (e in o){
2135                 oe = o[e];
2136                 if (!me.filterOptRe.test(e)) {
2137                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2138                 }
2139             }
2140         } else {
2141             eventName = eventName.toLowerCase();
2142             ce = me.events[eventName] || TRUE;
2143             if (Ext.isBoolean(ce)) {
2144                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2145             }
2146             ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
2147         }
2148     },
2149
2150     /**
2151      * Removes an event handler.
2152      * @param {String}   eventName The type of event the handler was associated with.
2153      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2154      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2155      */
2156     removeListener : function(eventName, fn, scope){
2157         var ce = this.events[eventName.toLowerCase()];
2158         if (ISOBJECT(ce)) {
2159             ce.removeListener(fn, scope);
2160         }
2161     },
2162
2163     /**
2164      * Removes all listeners for this object
2165      */
2166     purgeListeners : function(){
2167         var events = this.events,
2168             evt,
2169             key;
2170         for(key in events){
2171             evt = events[key];
2172             if(ISOBJECT(evt)){
2173                 evt.clearListeners();
2174             }
2175         }
2176     },
2177
2178     /**
2179      * Adds the specified events to the list of events which this Observable may fire.
2180      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2181      * or the first event name string if multiple event names are being passed as separate parameters.
2182      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2183      * Usage:<pre><code>
2184 this.addEvents('storeloaded', 'storecleared');
2185 </code></pre>
2186      */
2187     addEvents : function(o){
2188         var me = this;
2189         me.events = me.events || {};
2190         if (Ext.isString(o)) {
2191             var a = arguments,
2192                 i = a.length;
2193             while(i--) {
2194                 me.events[a[i]] = me.events[a[i]] || TRUE;
2195             }
2196         } else {
2197             Ext.applyIf(me.events, o);
2198         }
2199     },
2200
2201     /**
2202      * Checks to see if this object has any listeners for a specified event
2203      * @param {String} eventName The name of the event to check for
2204      * @return {Boolean} True if the event is being listened for, else false
2205      */
2206     hasListener : function(eventName){
2207         var e = this.events[eventName.toLowerCase()];
2208         return ISOBJECT(e) && e.listeners.length > 0;
2209     },
2210
2211     /**
2212      * Suspend the firing of all events. (see {@link #resumeEvents})
2213      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2214      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2215      */
2216     suspendEvents : function(queueSuspended){
2217         this.eventsSuspended = TRUE;
2218         if(queueSuspended && !this.eventQueue){
2219             this.eventQueue = [];
2220         }
2221     },
2222
2223     /**
2224      * Resume firing events. (see {@link #suspendEvents})
2225      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2226      * events fired during event suspension will be sent to any listeners now.
2227      */
2228     resumeEvents : function(){
2229         var me = this,
2230             queued = me.eventQueue || [];
2231         me.eventsSuspended = FALSE;
2232         delete me.eventQueue;
2233         EACH(queued, function(e) {
2234             me.fireEvent.apply(me, e);
2235         });
2236     }
2237 };
2238
2239 var OBSERVABLE = EXTUTIL.Observable.prototype;
2240 /**
2241  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2242  * @param {String}   eventName     The type of event to listen for
2243  * @param {Function} handler       The method the event invokes
2244  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2245  * <b>If omitted, defaults to the object which fired the event.</b>
2246  * @param {Object}   options       (optional) An object containing handler configuration.
2247  * @method
2248  */
2249 OBSERVABLE.on = OBSERVABLE.addListener;
2250 /**
2251  * Removes an event handler (shorthand for {@link #removeListener}.)
2252  * @param {String}   eventName     The type of event the handler was associated with.
2253  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2254  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2255  * @method
2256  */
2257 OBSERVABLE.un = OBSERVABLE.removeListener;
2258
2259 /**
2260  * Removes <b>all</b> added captures from the Observable.
2261  * @param {Observable} o The Observable to release
2262  * @static
2263  */
2264 EXTUTIL.Observable.releaseCapture = function(o){
2265     o.fireEvent = OBSERVABLE.fireEvent;
2266 };
2267
2268 function createTargeted(h, o, scope){
2269     return function(){
2270         if(o.target == arguments[0]){
2271             h.apply(scope, TOARRAY(arguments));
2272         }
2273     };
2274 };
2275
2276 function createBuffered(h, o, l, scope){
2277     l.task = new EXTUTIL.DelayedTask();
2278     return function(){
2279         l.task.delay(o.buffer, h, scope, TOARRAY(arguments));
2280     };
2281 };
2282
2283 function createSingle(h, e, fn, scope){
2284     return function(){
2285         e.removeListener(fn, scope);
2286         return h.apply(scope, arguments);
2287     };
2288 };
2289
2290 function createDelayed(h, o, l, scope){
2291     return function(){
2292         var task = new EXTUTIL.DelayedTask();
2293         if(!l.tasks) {
2294             l.tasks = [];
2295         }
2296         l.tasks.push(task);
2297         task.delay(o.delay || 10, h, scope, TOARRAY(arguments));
2298     };
2299 };
2300
2301 EXTUTIL.Event = function(obj, name){
2302     this.name = name;
2303     this.obj = obj;
2304     this.listeners = [];
2305 };
2306
2307 EXTUTIL.Event.prototype = {
2308     addListener : function(fn, scope, options){
2309         var me = this,
2310             l;
2311         scope = scope || me.obj;
2312         if(!me.isListening(fn, scope)){
2313             l = me.createListener(fn, scope, options);
2314             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2315                 me.listeners = me.listeners.slice(0);
2316             }
2317             me.listeners.push(l);
2318         }
2319     },
2320
2321     createListener: function(fn, scope, o){
2322         o = o || {}, scope = scope || this.obj;
2323         var l = {
2324             fn: fn,
2325             scope: scope,
2326             options: o
2327         }, h = fn;
2328         if(o.target){
2329             h = createTargeted(h, o, scope);
2330         }
2331         if(o.delay){
2332             h = createDelayed(h, o, l, scope);
2333         }
2334         if(o.single){
2335             h = createSingle(h, this, fn, scope);
2336         }
2337         if(o.buffer){
2338             h = createBuffered(h, o, l, scope);
2339         }
2340         l.fireFn = h;
2341         return l;
2342     },
2343
2344     findListener : function(fn, scope){
2345         var list = this.listeners,
2346             i = list.length,
2347             l;
2348             
2349         scope = scope || this.obj;
2350         while(i--){
2351             l = list[i];
2352             if(l){
2353                 if(l.fn == fn && l.scope == scope){
2354                     return i;
2355                 }
2356             }
2357         }
2358         return -1;
2359     },
2360
2361     isListening : function(fn, scope){
2362         return this.findListener(fn, scope) != -1;
2363     },
2364
2365     removeListener : function(fn, scope){
2366         var index,
2367             l,
2368             k,
2369             me = this,
2370             ret = FALSE;
2371         if((index = me.findListener(fn, scope)) != -1){
2372             if (me.firing) {
2373                 me.listeners = me.listeners.slice(0);
2374             }
2375             l = me.listeners[index];
2376             if(l.task) {
2377                 l.task.cancel();
2378                 delete l.task;
2379             }
2380             k = l.tasks && l.tasks.length;
2381             if(k) {
2382                 while(k--) {
2383                     l.tasks[k].cancel();
2384                 }
2385                 delete l.tasks;
2386             }
2387             me.listeners.splice(index, 1);
2388             ret = TRUE;
2389         }
2390         return ret;
2391     },
2392
2393     // Iterate to stop any buffered/delayed events
2394     clearListeners : function(){
2395         var me = this,
2396             l = me.listeners,
2397             i = l.length;
2398         while(i--) {
2399             me.removeListener(l[i].fn, l[i].scope);
2400         }
2401     },
2402
2403     fire : function(){
2404         var me = this,
2405             args = TOARRAY(arguments),
2406             listeners = me.listeners,
2407             len = listeners.length,
2408             i = 0,
2409             l;
2410
2411         if(len > 0){
2412             me.firing = TRUE;
2413             for (; i < len; i++) {
2414                 l = listeners[i];
2415                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2416                     return (me.firing = FALSE);
2417                 }
2418             }
2419         }
2420         me.firing = FALSE;
2421         return TRUE;
2422     }
2423 };
2424 })();/**
2425  * @class Ext.util.Observable
2426  */
2427 Ext.apply(Ext.util.Observable.prototype, function(){
2428     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2429     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2430     // private
2431     function getMethodEvent(method){
2432         var e = (this.methodEvents = this.methodEvents ||
2433         {})[method], returnValue, v, cancel, obj = this;
2434
2435         if (!e) {
2436             this.methodEvents[method] = e = {};
2437             e.originalFn = this[method];
2438             e.methodName = method;
2439             e.before = [];
2440             e.after = [];
2441
2442             var makeCall = function(fn, scope, args){
2443                 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {
2444                     if (Ext.isObject(v)) {
2445                         returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;
2446                         cancel = !!v.cancel;
2447                     }
2448                     else
2449                         if (v === false) {
2450                             cancel = true;
2451                         }
2452                         else {
2453                             returnValue = v;
2454                         }
2455                 }
2456             };
2457
2458             this[method] = function(){
2459                 var args = Ext.toArray(arguments);
2460                 returnValue = v = undefined;
2461                 cancel = false;
2462
2463                 Ext.each(e.before, function(b){
2464                     makeCall(b.fn, b.scope, args);
2465                     if (cancel) {
2466                         return returnValue;
2467                     }
2468                 });
2469
2470                 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {
2471                     returnValue = v;
2472                 }
2473                 Ext.each(e.after, function(a){
2474                     makeCall(a.fn, a.scope, args);
2475                     if (cancel) {
2476                         return returnValue;
2477                     }
2478                 });
2479                 return returnValue;
2480             };
2481         }
2482         return e;
2483     }
2484
2485     return {
2486         // these are considered experimental
2487         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2488         // adds an 'interceptor' called before the original method
2489         beforeMethod : function(method, fn, scope){
2490             getMethodEvent.call(this, method).before.push({
2491                 fn: fn,
2492                 scope: scope
2493             });
2494         },
2495
2496         // adds a 'sequence' called after the original method
2497         afterMethod : function(method, fn, scope){
2498             getMethodEvent.call(this, method).after.push({
2499                 fn: fn,
2500                 scope: scope
2501             });
2502         },
2503
2504         removeMethodListener: function(method, fn, scope){
2505             var e = getMethodEvent.call(this, method), found = false;
2506             Ext.each(e.before, function(b, i, arr){
2507                 if (b.fn == fn && b.scope == scope) {
2508                     arr.splice(i, 1);
2509                     found = true;
2510                     return false;
2511                 }
2512             });
2513             if (!found) {
2514                 Ext.each(e.after, function(a, i, arr){
2515                     if (a.fn == fn && a.scope == scope) {
2516                         arr.splice(i, 1);
2517                         return false;
2518                     }
2519                 });
2520             }
2521         },
2522
2523         /**
2524          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2525          * @param {Object} o The Observable whose events this object is to relay.
2526          * @param {Array} events Array of event names to relay.
2527          */
2528         relayEvents : function(o, events){
2529             var me = this;
2530             function createHandler(ename){
2531                 return function(){
2532                     return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));
2533                 };
2534             }
2535             Ext.each(events, function(ename){
2536                 me.events[ename] = me.events[ename] || true;
2537                 o.on(ename, createHandler(ename), me);
2538             });
2539         },
2540
2541         /**
2542          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2543          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2544          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2545          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2546          * access the required target more quickly.</p>
2547          * <p>Example:</p><pre><code>
2548 Ext.override(Ext.form.Field, {
2549     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble
2550     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2551         this.enableBubble('change');
2552     }),
2553
2554     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.
2555     getBubbleTarget : function() {
2556         if (!this.formPanel) {
2557             this.formPanel = this.findParentByType('form');
2558         }
2559         return this.formPanel;
2560     }
2561 });
2562
2563 var myForm = new Ext.formPanel({
2564     title: 'User Details',
2565     items: [{
2566         ...
2567     }],
2568     listeners: {
2569         change: function() {
2570             // Title goes red if form has been modified.
2571             myForm.header.setStyle('color', 'red');
2572         }
2573     }
2574 });
2575 </code></pre>
2576          * @param {String/Array} events The event name to bubble, or an Array of event names.
2577          */
2578         enableBubble : function(events){
2579             var me = this;
2580             if(!Ext.isEmpty(events)){
2581                 events = Ext.isArray(events) ? events : Ext.toArray(arguments);
2582                 Ext.each(events, function(ename){
2583                     ename = ename.toLowerCase();
2584                     var ce = me.events[ename] || true;
2585                     if (Ext.isBoolean(ce)) {
2586                         ce = new Ext.util.Event(me, ename);
2587                         me.events[ename] = ce;
2588                     }
2589                     ce.bubble = true;
2590                 });
2591             }
2592         }
2593     };
2594 }());
2595
2596
2597 /**
2598  * Starts capture on the specified Observable. All events will be passed
2599  * to the supplied function with the event name + standard signature of the event
2600  * <b>before</b> the event is fired. If the supplied function returns false,
2601  * the event will not fire.
2602  * @param {Observable} o The Observable to capture events from.
2603  * @param {Function} fn The function to call when an event is fired.
2604  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2605  * @static
2606  */
2607 Ext.util.Observable.capture = function(o, fn, scope){
2608     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2609 };
2610
2611
2612 /**
2613  * Sets observability on the passed class constructor.<p>
2614  * <p>This makes any event fired on any instance of the passed class also fire a single event through
2615  * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2616  * <p>Usage:</p><pre><code>
2617 Ext.util.Observable.observeClass(Ext.data.Connection);
2618 Ext.data.Connection.on('beforerequest', function(con, options) {
2619     console.log('Ajax request made to ' + options.url);
2620 });</code></pre>
2621  * @param {Function} c The class constructor to make observable.
2622  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}. 
2623  * @static
2624  */
2625 Ext.util.Observable.observeClass = function(c, listeners){
2626     if(c){
2627       if(!c.fireEvent){
2628           Ext.apply(c, new Ext.util.Observable());
2629           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2630       }
2631       if(Ext.isObject(listeners)){
2632           c.on(listeners);
2633       }
2634       return c;
2635    }
2636 };/**
2637  * @class Ext.EventManager
2638  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2639  * several useful events directly.
2640  * See {@link Ext.EventObject} for more details on normalized event objects.
2641  * @singleton
2642  */
2643
2644 Ext.EventManager = function(){
2645     var docReadyEvent,
2646         docReadyProcId,
2647         docReadyState = false,
2648         DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2649         E = Ext.lib.Event,
2650         D = Ext.lib.Dom,
2651         DOC = document,
2652         WINDOW = window,
2653         DOMCONTENTLOADED = "DOMContentLoaded",
2654         COMPLETE = 'complete',
2655         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2656         /*
2657          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2658          * a reference to them so we can look them up at a later point.
2659          */
2660         specialElCache = [];
2661
2662      function getId(el){
2663         var id = false,
2664             i = 0,
2665             len = specialElCache.length,
2666             id = false,
2667             skip = false,
2668             o;
2669         if(el){
2670             if(el.getElementById || el.navigator){
2671                 // look up the id
2672                 for(; i < len; ++i){
2673                     o = specialElCache[i];
2674                     if(o.el === el){
2675                         id = o.id;
2676                         break;
2677                     }
2678                 }
2679                 if(!id){
2680                     // for browsers that support it, ensure that give the el the same id
2681                     id = Ext.id(el);
2682                     specialElCache.push({
2683                         id: id,
2684                         el: el
2685                     });
2686                     skip = true;
2687                 }
2688             }else{
2689                 id = Ext.id(el);
2690             }
2691             if(!Ext.elCache[id]){
2692                 Ext.Element.addToCache(new Ext.Element(el), id);
2693                 if(skip){
2694                     Ext.elCache[id].skipGC = true;
2695                 }
2696             }
2697         }
2698         return id;
2699      };
2700
2701     /// There is some jquery work around stuff here that isn't needed in Ext Core.
2702     function addListener(el, ename, fn, task, wrap, scope){
2703         el = Ext.getDom(el);
2704         var id = getId(el),
2705             es = Ext.elCache[id].events,
2706             wfn;
2707
2708         wfn = E.on(el, ename, wrap);
2709         es[ename] = es[ename] || [];
2710
2711         /* 0 = Original Function,
2712            1 = Event Manager Wrapped Function,
2713            2 = Scope,
2714            3 = Adapter Wrapped Function,
2715            4 = Buffered Task
2716         */
2717         es[ename].push([fn, wrap, scope, wfn, task]);
2718
2719         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2720         // without breaking ExtJS.
2721
2722         // workaround for jQuery
2723         if(el.addEventListener && ename == "mousewheel"){
2724             var args = ["DOMMouseScroll", wrap, false];
2725             el.addEventListener.apply(el, args);
2726             Ext.EventManager.addListener(WINDOW, 'unload', function(){
2727                 el.removeEventListener.apply(el, args);
2728             });
2729         }
2730
2731         // fix stopped mousedowns on the document
2732         if(el == DOC && ename == "mousedown"){
2733             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2734         }
2735     };
2736
2737     function doScrollChk(){
2738         /* Notes:
2739              'doScroll' will NOT work in a IFRAME/FRAMESET.
2740              The method succeeds but, a DOM query done immediately after -- FAILS.
2741           */
2742         if(window != top){
2743             return false;
2744         }
2745
2746         try{
2747             DOC.documentElement.doScroll('left');
2748         }catch(e){
2749              return false;
2750         }
2751
2752         fireDocReady();
2753         return true;
2754     }
2755     /**
2756      * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2757      * be true by other means). If false, the state is evaluated again until canceled.
2758      */
2759     function checkReadyState(e){
2760
2761         if(Ext.isIE && doScrollChk()){
2762             return true;
2763         }
2764         if(DOC.readyState == COMPLETE){
2765             fireDocReady();
2766             return true;
2767         }
2768         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2769         return false;
2770     }
2771
2772     var styles;
2773     function checkStyleSheets(e){
2774         styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2775         if(styles.length == DOC.styleSheets.length){
2776             fireDocReady();
2777             return true;
2778         }
2779         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2780         return false;
2781     }
2782
2783     function OperaDOMContentLoaded(e){
2784         DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2785         checkStyleSheets();
2786     }
2787
2788     function fireDocReady(e){
2789         if(!docReadyState){
2790             docReadyState = true; //only attempt listener removal once
2791
2792             if(docReadyProcId){
2793                 clearTimeout(docReadyProcId);
2794             }
2795             if(DETECT_NATIVE) {
2796                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2797             }
2798             if(Ext.isIE && checkReadyState.bindIE){  //was this was actually set ??
2799                 DOC.detachEvent('onreadystatechange', checkReadyState);
2800             }
2801             E.un(WINDOW, "load", arguments.callee);
2802         }
2803         if(docReadyEvent && !Ext.isReady){
2804             Ext.isReady = true;
2805             docReadyEvent.fire();
2806             docReadyEvent.listeners = [];
2807         }
2808
2809     };
2810
2811     function initDocReady(){
2812         docReadyEvent || (docReadyEvent = new Ext.util.Event());
2813         if (DETECT_NATIVE) {
2814             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2815         }
2816         /*
2817          * Handle additional (exceptional) detection strategies here
2818          */
2819         if (Ext.isIE){
2820             //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2821             //See if page is already loaded
2822             if(!checkReadyState()){
2823                 checkReadyState.bindIE = true;
2824                 DOC.attachEvent('onreadystatechange', checkReadyState);
2825             }
2826
2827         }else if(Ext.isOpera ){
2828             /* Notes:
2829                Opera needs special treatment needed here because CSS rules are NOT QUITE
2830                available after DOMContentLoaded is raised.
2831             */
2832
2833             //See if page is already loaded and all styleSheets are in place
2834             (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2835                 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2836
2837         }else if (Ext.isWebKit){
2838             //Fallback for older Webkits without DOMCONTENTLOADED support
2839             checkReadyState();
2840         }
2841         // no matter what, make sure it fires on load
2842         E.on(WINDOW, "load", fireDocReady);
2843     };
2844
2845     function createTargeted(h, o){
2846         return function(){
2847             var args = Ext.toArray(arguments);
2848             if(o.target == Ext.EventObject.setEvent(args[0]).target){
2849                 h.apply(this, args);
2850             }
2851         };
2852     };
2853
2854     function createBuffered(h, o, task){
2855         return function(e){
2856             // create new event object impl so new events don't wipe out properties
2857             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2858         };
2859     };
2860
2861     function createSingle(h, el, ename, fn, scope){
2862         return function(e){
2863             Ext.EventManager.removeListener(el, ename, fn, scope);
2864             h(e);
2865         };
2866     };
2867
2868     function createDelayed(h, o, fn){
2869         return function(e){
2870             var task = new Ext.util.DelayedTask(h);
2871             if(!fn.tasks) {
2872                 fn.tasks = [];
2873             }
2874             fn.tasks.push(task);
2875             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2876         };
2877     };
2878
2879     function listen(element, ename, opt, fn, scope){
2880         var o = !Ext.isObject(opt) ? {} : opt,
2881             el = Ext.getDom(element), task;
2882
2883         fn = fn || o.fn;
2884         scope = scope || o.scope;
2885
2886         if(!el){
2887             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2888         }
2889         function h(e){
2890             // prevent errors while unload occurring
2891             if(!Ext){// !window[xname]){  ==> can't we do this?
2892                 return;
2893             }
2894             e = Ext.EventObject.setEvent(e);
2895             var t;
2896             if (o.delegate) {
2897                 if(!(t = e.getTarget(o.delegate, el))){
2898                     return;
2899                 }
2900             } else {
2901                 t = e.target;
2902             }
2903             if (o.stopEvent) {
2904                 e.stopEvent();
2905             }
2906             if (o.preventDefault) {
2907                e.preventDefault();
2908             }
2909             if (o.stopPropagation) {
2910                 e.stopPropagation();
2911             }
2912             if (o.normalized) {
2913                 e = e.browserEvent;
2914             }
2915
2916             fn.call(scope || el, e, t, o);
2917         };
2918         if(o.target){
2919             h = createTargeted(h, o);
2920         }
2921         if(o.delay){
2922             h = createDelayed(h, o, fn);
2923         }
2924         if(o.single){
2925             h = createSingle(h, el, ename, fn, scope);
2926         }
2927         if(o.buffer){
2928             task = new Ext.util.DelayedTask(h);
2929             h = createBuffered(h, o, task);
2930         }
2931
2932         addListener(el, ename, fn, task, h, scope);
2933         return h;
2934     };
2935
2936     var pub = {
2937         /**
2938          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
2939          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2940          * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2941          * @param {String} eventName The name of the event to listen for.
2942          * @param {Function} handler The handler function the event invokes. This function is passed
2943          * the following parameters:<ul>
2944          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2945          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2946          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2947          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2948          * </ul>
2949          * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.
2950          * @param {Object} options (optional) An object containing handler configuration properties.
2951          * This may contain any of the following properties:<ul>
2952          * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>
2953          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2954          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2955          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2956          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2957          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2958          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2959          * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
2960          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2961          * by the specified number of milliseconds. If the event fires again within that time, the original
2962          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2963          * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
2964          * </ul><br>
2965          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2966          */
2967         addListener : function(element, eventName, fn, scope, options){
2968             if(Ext.isObject(eventName)){
2969                 var o = eventName, e, val;
2970                 for(e in o){
2971                     val = o[e];
2972                     if(!propRe.test(e)){
2973                         if(Ext.isFunction(val)){
2974                             // shared options
2975                             listen(element, e, o, val, o.scope);
2976                         }else{
2977                             // individual options
2978                             listen(element, e, val);
2979                         }
2980                     }
2981                 }
2982             } else {
2983                 listen(element, eventName, options, fn, scope);
2984             }
2985         },
2986
2987         /**
2988          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
2989          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
2990          * @param {String/HTMLElement} el The id or html element from which to remove the listener.
2991          * @param {String} eventName The name of the event.
2992          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2993          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2994          * then this must refer to the same object.
2995          */
2996         removeListener : function(el, eventName, fn, scope){
2997             el = Ext.getDom(el);
2998             var id = getId(el),
2999                 f = el && (Ext.elCache[id].events)[eventName] || [],
3000                 wrap, i, l, k, len, fnc;
3001
3002             for (i = 0, len = f.length; i < len; i++) {
3003
3004                 /* 0 = Original Function,
3005                    1 = Event Manager Wrapped Function,
3006                    2 = Scope,
3007                    3 = Adapter Wrapped Function,
3008                    4 = Buffered Task
3009                 */
3010                 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3011                     if(fnc[4]) {
3012                         fnc[4].cancel();
3013                     }
3014                     k = fn.tasks && fn.tasks.length;
3015                     if(k) {
3016                         while(k--) {
3017                             fn.tasks[k].cancel();
3018                         }
3019                         delete fn.tasks;
3020                     }
3021                     wrap = fnc[1];
3022                     E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3023
3024                     // jQuery workaround that should be removed from Ext Core
3025                     if(wrap && el.addEventListener && eventName == "mousewheel"){
3026                         el.removeEventListener("DOMMouseScroll", wrap, false);
3027                     }
3028
3029                     // fix stopped mousedowns on the document
3030                     if(wrap && el == DOC && eventName == "mousedown"){
3031                         Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3032                     }
3033
3034                     f.splice(i, 1);
3035                     if (f.length === 0) {
3036                         delete Ext.elCache[id].events[eventName];
3037                     }
3038                     for (k in Ext.elCache[id].events) {
3039                         return false;
3040                     }
3041                     Ext.elCache[id].events = {};
3042                     return false;
3043                 }
3044             }
3045         },
3046
3047         /**
3048          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
3049          * directly on an Element in favor of calling this version.
3050          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3051          */
3052         removeAll : function(el){
3053             el = Ext.getDom(el);
3054             var id = getId(el),
3055                 ec = Ext.elCache[id] || {},
3056                 es = ec.events || {},
3057                 f, i, len, ename, fn, k, wrap;
3058
3059             for(ename in es){
3060                 if(es.hasOwnProperty(ename)){
3061                     f = es[ename];
3062                     /* 0 = Original Function,
3063                        1 = Event Manager Wrapped Function,
3064                        2 = Scope,
3065                        3 = Adapter Wrapped Function,
3066                        4 = Buffered Task
3067                     */
3068                     for (i = 0, len = f.length; i < len; i++) {
3069                         fn = f[i];
3070                         if(fn[4]) {
3071                             fn[4].cancel();
3072                         }
3073                         if(fn[0].tasks && (k = fn[0].tasks.length)) {
3074                             while(k--) {
3075                                 fn[0].tasks[k].cancel();
3076                             }
3077                             delete fn.tasks;
3078                         }
3079                         wrap =  fn[1];
3080                         E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3081
3082                         // jQuery workaround that should be removed from Ext Core
3083                         if(el.addEventListener && wrap && ename == "mousewheel"){
3084                             el.removeEventListener("DOMMouseScroll", wrap, false);
3085                         }
3086
3087                         // fix stopped mousedowns on the document
3088                         if(wrap && el == DOC &&  ename == "mousedown"){
3089                             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3090                         }
3091                     }
3092                 }
3093             }
3094             if (Ext.elCache[id]) {
3095                 Ext.elCache[id].events = {};
3096             }
3097         },
3098
3099         getListeners : function(el, eventName) {
3100             el = Ext.getDom(el);
3101             var id = getId(el),
3102                 ec = Ext.elCache[id] || {},
3103                 es = ec.events || {},
3104                 results = [];
3105             if (es && es[eventName]) {
3106                 return es[eventName];
3107             } else {
3108                 return null;
3109             }
3110         },
3111
3112         purgeElement : function(el, recurse, eventName) {
3113             el = Ext.getDom(el);
3114             var id = getId(el),
3115                 ec = Ext.elCache[id] || {},
3116                 es = ec.events || {},
3117                 i, f, len;
3118             if (eventName) {
3119                 if (es && es.hasOwnProperty(eventName)) {
3120                     f = es[eventName];
3121                     for (i = 0, len = f.length; i < len; i++) {
3122                         Ext.EventManager.removeListener(el, eventName, f[i][0]);
3123                     }
3124                 }
3125             } else {
3126                 Ext.EventManager.removeAll(el);
3127             }
3128             if (recurse && el && el.childNodes) {
3129                 for (i = 0, len = el.childNodes.length; i < len; i++) {
3130                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3131                 }
3132             }
3133         },
3134
3135         _unload : function() {
3136             var el;
3137             for (el in Ext.elCache) {
3138                 Ext.EventManager.removeAll(el);
3139             }
3140             delete Ext.elCache;
3141             delete Ext.Element._flyweights;
3142
3143             // Abort any outstanding Ajax requests
3144             var c,
3145                 conn,
3146                 tid,
3147                 ajax = Ext.lib.Ajax;
3148             (Ext.isObject(ajax.conn)) ? conn = ajax.conn : conn = {};
3149             for (tid in conn) {
3150                 c = conn[tid];
3151                 if (c) {
3152                     ajax.abort({conn: c, tId: tid});
3153                 }
3154             }
3155         },
3156         /**
3157          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3158          * accessed shorthanded as Ext.onReady().
3159          * @param {Function} fn The method the event invokes.
3160          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3161          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3162          * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3163          */
3164         onDocumentReady : function(fn, scope, options){
3165             if(Ext.isReady){ // if it already fired or document.body is present
3166                 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3167                 docReadyEvent.addListener(fn, scope, options);
3168                 docReadyEvent.fire();
3169                 docReadyEvent.listeners = [];
3170             }else{
3171                 if(!docReadyEvent){
3172                     initDocReady();
3173                 }
3174                 options = options || {};
3175                 options.delay = options.delay || 1;
3176                 docReadyEvent.addListener(fn, scope, options);
3177             }
3178         },
3179
3180         /**
3181          * Forces a document ready state transition for the framework.  Used when Ext is loaded
3182          * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3183          * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3184          */
3185         fireDocReady  : fireDocReady
3186     };
3187      /**
3188      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
3189      * @param {String/HTMLElement} el The html element or id to assign the event handler to
3190      * @param {String} eventName The name of the event to listen for.
3191      * @param {Function} handler The handler function the event invokes.
3192      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3193      * @param {Object} options (optional) An object containing standard {@link #addListener} options
3194      * @member Ext.EventManager
3195      * @method on
3196      */
3197     pub.on = pub.addListener;
3198     /**
3199      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
3200      * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3201      * @param {String} eventName The name of the event.
3202      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3203      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3204      * then this must refer to the same object.
3205      * @member Ext.EventManager
3206      * @method un
3207      */
3208     pub.un = pub.removeListener;
3209
3210     pub.stoppedMouseDownEvent = new Ext.util.Event();
3211     return pub;
3212 }();
3213 /**
3214   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3215   * @param {Function} fn The method the event invokes.
3216   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3217   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3218   * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3219   * @member Ext
3220   * @method onReady
3221  */
3222 Ext.onReady = Ext.EventManager.onDocumentReady;
3223
3224
3225 //Initialize doc classes
3226 (function(){
3227
3228     var initExtCss = function(){
3229         // find the body element
3230         var bd = document.body || document.getElementsByTagName('body')[0];
3231         if(!bd){ return false; }
3232         var cls = [' ',
3233                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3234                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3235                 : Ext.isOpera ? "ext-opera"
3236                 : Ext.isWebKit ? "ext-webkit" : ""];
3237
3238         if(Ext.isSafari){
3239             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3240         }else if(Ext.isChrome){
3241             cls.push("ext-chrome");
3242         }
3243
3244         if(Ext.isMac){
3245             cls.push("ext-mac");
3246         }
3247         if(Ext.isLinux){
3248             cls.push("ext-linux");
3249         }
3250
3251         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3252             var p = bd.parentNode;
3253             if(p){
3254                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3255             }
3256         }
3257         bd.className += cls.join(' ');
3258         return true;
3259     }
3260
3261     if(!initExtCss()){
3262         Ext.onReady(initExtCss);
3263     }
3264 })();
3265
3266
3267 /**
3268  * @class Ext.EventObject
3269  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3270  * wraps the browser's native event-object normalizing cross-browser differences,
3271  * such as which mouse button is clicked, keys pressed, mechanisms to stop
3272  * event-propagation along with a method to prevent default actions from taking place.
3273  * <p>For example:</p>
3274  * <pre><code>
3275 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3276     e.preventDefault();
3277     var target = e.getTarget(); // same as t (the target HTMLElement)
3278     ...
3279 }
3280 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
3281 myDiv.on(         // 'on' is shorthand for addListener
3282     "click",      // perform an action on click of myDiv
3283     handleClick   // reference to the action handler
3284 );
3285 // other methods to do the same:
3286 Ext.EventManager.on("myDiv", 'click', handleClick);
3287 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3288  </code></pre>
3289  * @singleton
3290  */
3291 Ext.EventObject = function(){
3292     var E = Ext.lib.Event,
3293         // safari keypress events for special keys return bad keycodes
3294         safariKeys = {
3295             3 : 13, // enter
3296             63234 : 37, // left
3297             63235 : 39, // right
3298             63232 : 38, // up
3299             63233 : 40, // down
3300             63276 : 33, // page up
3301             63277 : 34, // page down
3302             63272 : 46, // delete
3303             63273 : 36, // home
3304             63275 : 35  // end
3305         },
3306         // normalize button clicks
3307         btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3308                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3309
3310     Ext.EventObjectImpl = function(e){
3311         if(e){
3312             this.setEvent(e.browserEvent || e);
3313         }
3314     };
3315
3316     Ext.EventObjectImpl.prototype = {
3317            /** @private */
3318         setEvent : function(e){
3319             var me = this;
3320             if(e == me || (e && e.browserEvent)){ // already wrapped
3321                 return e;
3322             }
3323             me.browserEvent = e;
3324             if(e){
3325                 // normalize buttons
3326                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3327                 if(e.type == 'click' && me.button == -1){
3328                     me.button = 0;
3329                 }
3330                 me.type = e.type;
3331                 me.shiftKey = e.shiftKey;
3332                 // mac metaKey behaves like ctrlKey
3333                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3334                 me.altKey = e.altKey;
3335                 // in getKey these will be normalized for the mac
3336                 me.keyCode = e.keyCode;
3337                 me.charCode = e.charCode;
3338                 // cache the target for the delayed and or buffered events
3339                 me.target = E.getTarget(e);
3340                 // same for XY
3341                 me.xy = E.getXY(e);
3342             }else{
3343                 me.button = -1;
3344                 me.shiftKey = false;
3345                 me.ctrlKey = false;
3346                 me.altKey = false;
3347                 me.keyCode = 0;
3348                 me.charCode = 0;
3349                 me.target = null;
3350                 me.xy = [0, 0];
3351             }
3352             return me;
3353         },
3354
3355         /**
3356          * Stop the event (preventDefault and stopPropagation)
3357          */
3358         stopEvent : function(){
3359             var me = this;
3360             if(me.browserEvent){
3361                 if(me.browserEvent.type == 'mousedown'){
3362                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3363                 }
3364                 E.stopEvent(me.browserEvent);
3365             }
3366         },
3367
3368         /**
3369          * Prevents the browsers default handling of the event.
3370          */
3371         preventDefault : function(){
3372             if(this.browserEvent){
3373                 E.preventDefault(this.browserEvent);
3374             }
3375         },
3376
3377         /**
3378          * Cancels bubbling of the event.
3379          */
3380         stopPropagation : function(){
3381             var me = this;
3382             if(me.browserEvent){
3383                 if(me.browserEvent.type == 'mousedown'){
3384                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3385                 }
3386                 E.stopPropagation(me.browserEvent);
3387             }
3388         },
3389
3390         /**
3391          * Gets the character code for the event.
3392          * @return {Number}
3393          */
3394         getCharCode : function(){
3395             return this.charCode || this.keyCode;
3396         },
3397
3398         /**
3399          * Returns a normalized keyCode for the event.
3400          * @return {Number} The key code
3401          */
3402         getKey : function(){
3403             return this.normalizeKey(this.keyCode || this.charCode)
3404         },
3405
3406         // private
3407         normalizeKey: function(k){
3408             return Ext.isSafari ? (safariKeys[k] || k) : k;
3409         },
3410
3411         /**
3412          * Gets the x coordinate of the event.
3413          * @return {Number}
3414          */
3415         getPageX : function(){
3416             return this.xy[0];
3417         },
3418
3419         /**
3420          * Gets the y coordinate of the event.
3421          * @return {Number}
3422          */
3423         getPageY : function(){
3424             return this.xy[1];
3425         },
3426
3427         /**
3428          * Gets the page coordinates of the event.
3429          * @return {Array} The xy values like [x, y]
3430          */
3431         getXY : function(){
3432             return this.xy;
3433         },
3434
3435         /**
3436          * Gets the target for the event.
3437          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3438          * @param {Number/Mixed} maxDepth (optional) The max depth to
3439                 search as a number or element (defaults to 10 || document.body)
3440          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3441          * @return {HTMLelement}
3442          */
3443         getTarget : function(selector, maxDepth, returnEl){
3444             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3445         },
3446
3447         /**
3448          * Gets the related target.
3449          * @return {HTMLElement}
3450          */
3451         getRelatedTarget : function(){
3452             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3453         },
3454
3455         /**
3456          * Normalizes mouse wheel delta across browsers
3457          * @return {Number} The delta
3458          */
3459         getWheelDelta : function(){
3460             var e = this.browserEvent;
3461             var delta = 0;
3462             if(e.wheelDelta){ /* IE/Opera. */
3463                 delta = e.wheelDelta/120;
3464             }else if(e.detail){ /* Mozilla case. */
3465                 delta = -e.detail/3;
3466             }
3467             return delta;
3468         },
3469
3470         /**
3471         * Returns true if the target of this event is a child of el.  Unless the allowEl parameter is set, it will return false if if the target is el.
3472         * Example usage:<pre><code>
3473         // Handle click on any child of an element
3474         Ext.getBody().on('click', function(e){
3475             if(e.within('some-el')){
3476                 alert('Clicked on a child of some-el!');
3477             }
3478         });
3479
3480         // Handle click directly on an element, ignoring clicks on child nodes
3481         Ext.getBody().on('click', function(e,t){
3482             if((t.id == 'some-el') && !e.within(t, true)){
3483                 alert('Clicked directly on some-el!');
3484             }
3485         });
3486         </code></pre>
3487          * @param {Mixed} el The id, DOM element or Ext.Element to check
3488          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3489          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3490          * @return {Boolean}
3491          */
3492         within : function(el, related, allowEl){
3493             if(el){
3494                 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3495                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3496             }
3497             return false;
3498         }
3499      };
3500
3501     return new Ext.EventObjectImpl();
3502 }();
3503 /**
3504 * @class Ext.EventManager
3505 */
3506 Ext.apply(Ext.EventManager, function(){
3507    var resizeEvent,
3508        resizeTask,
3509        textEvent,
3510        textSize,
3511        D = Ext.lib.Dom,
3512        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3513        curWidth = 0,
3514        curHeight = 0,
3515        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3516        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3517        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3518        useKeydown = Ext.isWebKit ?
3519                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3520                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3521
3522    return {
3523        // private
3524        doResizeEvent: function(){
3525            var h = D.getViewHeight(),
3526                w = D.getViewWidth();
3527
3528             //whacky problem in IE where the resize event will fire even though the w/h are the same.
3529             if(curHeight != h || curWidth != w){
3530                resizeEvent.fire(curWidth = w, curHeight = h);
3531             }
3532        },
3533
3534        /**
3535         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3536         * passes new viewport width and height to handlers.
3537         * @param {Function} fn      The handler function the window resize event invokes.
3538         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3539         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3540         */
3541        onWindowResize : function(fn, scope, options){
3542            if(!resizeEvent){
3543                resizeEvent = new Ext.util.Event();
3544                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3545                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3546            }
3547            resizeEvent.addListener(fn, scope, options);
3548        },
3549
3550        // exposed only to allow manual firing
3551        fireWindowResize : function(){
3552            if(resizeEvent){
3553                resizeTask.delay(100);
3554            }
3555        },
3556
3557        /**
3558         * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
3559         * @param {Function} fn      The function the event invokes.
3560         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3561         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3562         */
3563        onTextResize : function(fn, scope, options){
3564            if(!textEvent){
3565                textEvent = new Ext.util.Event();
3566                var textEl = new Ext.Element(document.createElement('div'));
3567                textEl.dom.className = 'x-text-resize';
3568                textEl.dom.innerHTML = 'X';
3569                textEl.appendTo(document.body);
3570                textSize = textEl.dom.offsetHeight;
3571                setInterval(function(){
3572                    if(textEl.dom.offsetHeight != textSize){
3573                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3574                    }
3575                }, this.textResizeInterval);
3576            }
3577            textEvent.addListener(fn, scope, options);
3578        },
3579
3580        /**
3581         * Removes the passed window resize listener.
3582         * @param {Function} fn        The method the event invokes
3583         * @param {Object}   scope    The scope of handler
3584         */
3585        removeResizeListener : function(fn, scope){
3586            if(resizeEvent){
3587                resizeEvent.removeListener(fn, scope);
3588            }
3589        },
3590
3591        // private
3592        fireResize : function(){
3593            if(resizeEvent){
3594                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3595            }
3596        },
3597
3598         /**
3599         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3600         */
3601        textResizeInterval : 50,
3602
3603        /**
3604         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3605         */
3606        ieDeferSrc : false,
3607
3608        // protected for use inside the framework
3609        // detects whether we should use keydown or keypress based on the browser.
3610        useKeydown: useKeydown
3611    };
3612 }());
3613
3614 Ext.EventManager.on = Ext.EventManager.addListener;
3615
3616
3617 Ext.apply(Ext.EventObjectImpl.prototype, {
3618    /** Key constant @type Number */
3619    BACKSPACE: 8,
3620    /** Key constant @type Number */
3621    TAB: 9,
3622    /** Key constant @type Number */
3623    NUM_CENTER: 12,
3624    /** Key constant @type Number */
3625    ENTER: 13,
3626    /** Key constant @type Number */
3627    RETURN: 13,
3628    /** Key constant @type Number */
3629    SHIFT: 16,
3630    /** Key constant @type Number */
3631    CTRL: 17,
3632    CONTROL : 17, // legacy
3633    /** Key constant @type Number */
3634    ALT: 18,
3635    /** Key constant @type Number */
3636    PAUSE: 19,
3637    /** Key constant @type Number */
3638    CAPS_LOCK: 20,
3639    /** Key constant @type Number */
3640    ESC: 27,
3641    /** Key constant @type Number */
3642    SPACE: 32,
3643    /** Key constant @type Number */
3644    PAGE_UP: 33,
3645    PAGEUP : 33, // legacy
3646    /** Key constant @type Number */
3647    PAGE_DOWN: 34,
3648    PAGEDOWN : 34, // legacy
3649    /** Key constant @type Number */
3650    END: 35,
3651    /** Key constant @type Number */
3652    HOME: 36,
3653    /** Key constant @type Number */
3654    LEFT: 37,
3655    /** Key constant @type Number */
3656    UP: 38,
3657    /** Key constant @type Number */
3658    RIGHT: 39,
3659    /** Key constant @type Number */
3660    DOWN: 40,
3661    /** Key constant @type Number */
3662    PRINT_SCREEN: 44,
3663    /** Key constant @type Number */
3664    INSERT: 45,
3665    /** Key constant @type Number */
3666    DELETE: 46,
3667    /** Key constant @type Number */
3668    ZERO: 48,
3669    /** Key constant @type Number */
3670    ONE: 49,
3671    /** Key constant @type Number */
3672    TWO: 50,
3673    /** Key constant @type Number */
3674    THREE: 51,
3675    /** Key constant @type Number */
3676    FOUR: 52,
3677    /** Key constant @type Number */
3678    FIVE: 53,
3679    /** Key constant @type Number */
3680    SIX: 54,
3681    /** Key constant @type Number */
3682    SEVEN: 55,
3683    /** Key constant @type Number */
3684    EIGHT: 56,
3685    /** Key constant @type Number */
3686    NINE: 57,
3687    /** Key constant @type Number */
3688    A: 65,
3689    /** Key constant @type Number */
3690    B: 66,
3691    /** Key constant @type Number */
3692    C: 67,
3693    /** Key constant @type Number */
3694    D: 68,
3695    /** Key constant @type Number */
3696    E: 69,
3697    /** Key constant @type Number */
3698    F: 70,
3699    /** Key constant @type Number */
3700    G: 71,
3701    /** Key constant @type Number */
3702    H: 72,
3703    /** Key constant @type Number */
3704    I: 73,
3705    /** Key constant @type Number */
3706    J: 74,
3707    /** Key constant @type Number */
3708    K: 75,
3709    /** Key constant @type Number */
3710    L: 76,
3711    /** Key constant @type Number */
3712    M: 77,
3713    /** Key constant @type Number */
3714    N: 78,
3715    /** Key constant @type Number */
3716    O: 79,
3717    /** Key constant @type Number */
3718    P: 80,
3719    /** Key constant @type Number */
3720    Q: 81,
3721    /** Key constant @type Number */
3722    R: 82,
3723    /** Key constant @type Number */
3724    S: 83,
3725    /** Key constant @type Number */
3726    T: 84,
3727    /** Key constant @type Number */
3728    U: 85,
3729    /** Key constant @type Number */
3730    V: 86,
3731    /** Key constant @type Number */
3732    W: 87,
3733    /** Key constant @type Number */
3734    X: 88,
3735    /** Key constant @type Number */
3736    Y: 89,
3737    /** Key constant @type Number */
3738    Z: 90,
3739    /** Key constant @type Number */
3740    CONTEXT_MENU: 93,
3741    /** Key constant @type Number */
3742    NUM_ZERO: 96,
3743    /** Key constant @type Number */
3744    NUM_ONE: 97,
3745    /** Key constant @type Number */
3746    NUM_TWO: 98,
3747    /** Key constant @type Number */
3748    NUM_THREE: 99,
3749    /** Key constant @type Number */
3750    NUM_FOUR: 100,
3751    /** Key constant @type Number */
3752    NUM_FIVE: 101,
3753    /** Key constant @type Number */
3754    NUM_SIX: 102,
3755    /** Key constant @type Number */
3756    NUM_SEVEN: 103,
3757    /** Key constant @type Number */
3758    NUM_EIGHT: 104,
3759    /** Key constant @type Number */
3760    NUM_NINE: 105,
3761    /** Key constant @type Number */
3762    NUM_MULTIPLY: 106,
3763    /** Key constant @type Number */
3764    NUM_PLUS: 107,
3765    /** Key constant @type Number */
3766    NUM_MINUS: 109,
3767    /** Key constant @type Number */
3768    NUM_PERIOD: 110,
3769    /** Key constant @type Number */
3770    NUM_DIVISION: 111,
3771    /** Key constant @type Number */
3772    F1: 112,
3773    /** Key constant @type Number */
3774    F2: 113,
3775    /** Key constant @type Number */
3776    F3: 114,
3777    /** Key constant @type Number */
3778    F4: 115,
3779    /** Key constant @type Number */
3780    F5: 116,
3781    /** Key constant @type Number */
3782    F6: 117,
3783    /** Key constant @type Number */
3784    F7: 118,
3785    /** Key constant @type Number */
3786    F8: 119,
3787    /** Key constant @type Number */
3788    F9: 120,
3789    /** Key constant @type Number */
3790    F10: 121,
3791    /** Key constant @type Number */
3792    F11: 122,
3793    /** Key constant @type Number */
3794    F12: 123,
3795
3796    /** @private */
3797    isNavKeyPress : function(){
3798        var me = this,
3799            k = this.normalizeKey(me.keyCode);
3800        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
3801        k == me.RETURN ||
3802        k == me.TAB ||
3803        k == me.ESC;
3804    },
3805
3806    isSpecialKey : function(){
3807        var k = this.normalizeKey(this.keyCode);
3808        return (this.type == 'keypress' && this.ctrlKey) ||
3809        this.isNavKeyPress() ||
3810        (k == this.BACKSPACE) || // Backspace
3811        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3812        (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
3813    },
3814
3815    getPoint : function(){
3816        return new Ext.lib.Point(this.xy[0], this.xy[1]);
3817    },
3818
3819    /**
3820     * Returns true if the control, meta, shift or alt key was pressed during this event.
3821     * @return {Boolean}
3822     */
3823    hasModifier : function(){
3824        return ((this.ctrlKey || this.altKey) || this.shiftKey);
3825    }
3826 });/**
3827  * @class Ext.Element
3828  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3829  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3830  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3831  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3832  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3833  * Usage:<br>
3834 <pre><code>
3835 // by id
3836 var el = Ext.get("my-div");
3837
3838 // by DOM element reference
3839 var el = Ext.get(myDivElement);
3840 </code></pre>
3841  * <b>Animations</b><br />
3842  * <p>When an element is manipulated, by default there is no animation.</p>
3843  * <pre><code>
3844 var el = Ext.get("my-div");
3845
3846 // no animation
3847 el.setWidth(100);
3848  * </code></pre>
3849  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
3850  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3851  * <pre><code>
3852 // default animation
3853 el.setWidth(100, true);
3854  * </code></pre>
3855  *
3856  * <p>To configure the effects, an object literal with animation options to use as the Element animation
3857  * configuration object can also be specified. Note that the supported Element animation configuration
3858  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
3859  * Element animation configuration options are:</p>
3860 <pre>
3861 Option    Default   Description
3862 --------- --------  ---------------------------------------------
3863 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
3864 {@link Ext.Fx#easing easing}    easeOut   The easing method
3865 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
3866 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
3867 </pre>
3868  *
3869  * <pre><code>
3870 // Element animation options object
3871 var opt = {
3872     {@link Ext.Fx#duration duration}: 1,
3873     {@link Ext.Fx#easing easing}: 'elasticIn',
3874     {@link Ext.Fx#callback callback}: this.foo,
3875     {@link Ext.Fx#scope scope}: this
3876 };
3877 // animation with some options set
3878 el.setWidth(100, opt);
3879  * </code></pre>
3880  * <p>The Element animation object being used for the animation will be set on the options
3881  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3882  * <pre><code>
3883 // using the "anim" property to get the Anim object
3884 if(opt.anim.isAnimated()){
3885     opt.anim.stop();
3886 }
3887  * </code></pre>
3888  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3889  * <p><b> Composite (Collections of) Elements</b></p>
3890  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3891  * @constructor Create a new Element directly.
3892  * @param {String/HTMLElement} element
3893  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
3894  */
3895 (function(){
3896 var DOC = document;
3897
3898 Ext.Element = function(element, forceNew){
3899     var dom = typeof element == "string" ?
3900               DOC.getElementById(element) : element,
3901         id;
3902
3903     if(!dom) return null;
3904
3905     id = dom.id;
3906
3907     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3908         return Ext.elCache[id].el;
3909     }
3910
3911     /**
3912      * The DOM element
3913      * @type HTMLElement
3914      */
3915     this.dom = dom;
3916
3917     /**
3918      * The DOM element ID
3919      * @type String
3920      */
3921     this.id = id || Ext.id(dom);
3922 };
3923
3924 var D = Ext.lib.Dom,
3925     DH = Ext.DomHelper,
3926     E = Ext.lib.Event,
3927     A = Ext.lib.Anim,
3928     El = Ext.Element,
3929     EC = Ext.elCache;
3930
3931 El.prototype = {
3932     /**
3933      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3934      * @param {Object} o The object with the attributes
3935      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3936      * @return {Ext.Element} this
3937      */
3938     set : function(o, useSet){
3939         var el = this.dom,
3940             attr,
3941             val,
3942             useSet = (useSet !== false) && !!el.setAttribute;
3943
3944         for(attr in o){
3945             if (o.hasOwnProperty(attr)) {
3946                 val = o[attr];
3947                 if (attr == 'style') {
3948                     DH.applyStyles(el, val);
3949                 } else if (attr == 'cls') {
3950                     el.className = val;
3951                 } else if (useSet) {
3952                     el.setAttribute(attr, val);
3953                 } else {
3954                     el[attr] = val;
3955                 }
3956             }
3957         }
3958         return this;
3959     },
3960
3961 //  Mouse events
3962     /**
3963      * @event click
3964      * Fires when a mouse click is detected within the element.
3965      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3966      * @param {HtmlElement} t The target of the event.
3967      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3968      */
3969     /**
3970      * @event contextmenu
3971      * Fires when a right click is detected within the element.
3972      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3973      * @param {HtmlElement} t The target of the event.
3974      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3975      */
3976     /**
3977      * @event dblclick
3978      * Fires when a mouse double click is detected within the element.
3979      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3980      * @param {HtmlElement} t The target of the event.
3981      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3982      */
3983     /**
3984      * @event mousedown
3985      * Fires when a mousedown is detected within the element.
3986      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3987      * @param {HtmlElement} t The target of the event.
3988      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3989      */
3990     /**
3991      * @event mouseup
3992      * Fires when a mouseup is detected within the element.
3993      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3994      * @param {HtmlElement} t The target of the event.
3995      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3996      */
3997     /**
3998      * @event mouseover
3999      * Fires when a mouseover is detected within the element.
4000      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4001      * @param {HtmlElement} t The target of the event.
4002      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4003      */
4004     /**
4005      * @event mousemove
4006      * Fires when a mousemove is detected with the element.
4007      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4008      * @param {HtmlElement} t The target of the event.
4009      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4010      */
4011     /**
4012      * @event mouseout
4013      * Fires when a mouseout is detected with the element.
4014      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4015      * @param {HtmlElement} t The target of the event.
4016      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4017      */
4018     /**
4019      * @event mouseenter
4020      * Fires when the mouse enters the element.
4021      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4022      * @param {HtmlElement} t The target of the event.
4023      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4024      */
4025     /**
4026      * @event mouseleave
4027      * Fires when the mouse leaves the element.
4028      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4029      * @param {HtmlElement} t The target of the event.
4030      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4031      */
4032
4033 //  Keyboard events
4034     /**
4035      * @event keypress
4036      * Fires when a keypress is detected within the element.
4037      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4038      * @param {HtmlElement} t The target of the event.
4039      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4040      */
4041     /**
4042      * @event keydown
4043      * Fires when a keydown is detected within the element.
4044      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4045      * @param {HtmlElement} t The target of the event.
4046      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4047      */
4048     /**
4049      * @event keyup
4050      * Fires when a keyup is detected within the element.
4051      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4052      * @param {HtmlElement} t The target of the event.
4053      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4054      */
4055
4056
4057 //  HTML frame/object events
4058     /**
4059      * @event load
4060      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
4061      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4062      * @param {HtmlElement} t The target of the event.
4063      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4064      */
4065     /**
4066      * @event unload
4067      * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
4068      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4069      * @param {HtmlElement} t The target of the event.
4070      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4071      */
4072     /**
4073      * @event abort
4074      * Fires when an object/image is stopped from loading before completely loaded.
4075      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4076      * @param {HtmlElement} t The target of the event.
4077      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4078      */
4079     /**
4080      * @event error
4081      * Fires when an object/image/frame cannot be loaded properly.
4082      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4083      * @param {HtmlElement} t The target of the event.
4084      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4085      */
4086     /**
4087      * @event resize
4088      * Fires when a document view is resized.
4089      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4090      * @param {HtmlElement} t The target of the event.
4091      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4092      */
4093     /**
4094      * @event scroll
4095      * Fires when a document view is scrolled.
4096      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4097      * @param {HtmlElement} t The target of the event.
4098      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4099      */
4100
4101 //  Form events
4102     /**
4103      * @event select
4104      * Fires when a user selects some text in a text field, including input and textarea.
4105      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4106      * @param {HtmlElement} t The target of the event.
4107      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4108      */
4109     /**
4110      * @event change
4111      * Fires when a control loses the input focus and its value has been modified since gaining focus.
4112      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4113      * @param {HtmlElement} t The target of the event.
4114      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4115      */
4116     /**
4117      * @event submit
4118      * Fires when a form is submitted.
4119      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4120      * @param {HtmlElement} t The target of the event.
4121      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4122      */
4123     /**
4124      * @event reset
4125      * Fires when a form is reset.
4126      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4127      * @param {HtmlElement} t The target of the event.
4128      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4129      */
4130     /**
4131      * @event focus
4132      * Fires when an element receives focus either via the pointing device or by tab navigation.
4133      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4134      * @param {HtmlElement} t The target of the event.
4135      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4136      */
4137     /**
4138      * @event blur
4139      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4140      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4141      * @param {HtmlElement} t The target of the event.
4142      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4143      */
4144
4145 //  User Interface events
4146     /**
4147      * @event DOMFocusIn
4148      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4149      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4150      * @param {HtmlElement} t The target of the event.
4151      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4152      */
4153     /**
4154      * @event DOMFocusOut
4155      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4156      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4157      * @param {HtmlElement} t The target of the event.
4158      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4159      */
4160     /**
4161      * @event DOMActivate
4162      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4163      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4164      * @param {HtmlElement} t The target of the event.
4165      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4166      */
4167
4168 //  DOM Mutation events
4169     /**
4170      * @event DOMSubtreeModified
4171      * Where supported. Fires when the subtree is modified.
4172      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4173      * @param {HtmlElement} t The target of the event.
4174      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4175      */
4176     /**
4177      * @event DOMNodeInserted
4178      * Where supported. Fires when a node has been added as a child of another node.
4179      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4180      * @param {HtmlElement} t The target of the event.
4181      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4182      */
4183     /**
4184      * @event DOMNodeRemoved
4185      * Where supported. Fires when a descendant node of the element is removed.
4186      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4187      * @param {HtmlElement} t The target of the event.
4188      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4189      */
4190     /**
4191      * @event DOMNodeRemovedFromDocument
4192      * Where supported. Fires when a node is being removed from a document.
4193      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4194      * @param {HtmlElement} t The target of the event.
4195      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4196      */
4197     /**
4198      * @event DOMNodeInsertedIntoDocument
4199      * Where supported. Fires when a node is being inserted into a document.
4200      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4201      * @param {HtmlElement} t The target of the event.
4202      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4203      */
4204     /**
4205      * @event DOMAttrModified
4206      * Where supported. Fires when an attribute has been modified.
4207      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4208      * @param {HtmlElement} t The target of the event.
4209      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4210      */
4211     /**
4212      * @event DOMCharacterDataModified
4213      * Where supported. Fires when the character data has been modified.
4214      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4215      * @param {HtmlElement} t The target of the event.
4216      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4217      */
4218
4219     /**
4220      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4221      * @type String
4222      */
4223     defaultUnit : "px",
4224
4225     /**
4226      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4227      * @param {String} selector The simple selector to test
4228      * @return {Boolean} True if this element matches the selector, else false
4229      */
4230     is : function(simpleSelector){
4231         return Ext.DomQuery.is(this.dom, simpleSelector);
4232     },
4233
4234     /**
4235      * Tries to focus the element. Any exceptions are caught and ignored.
4236      * @param {Number} defer (optional) Milliseconds to defer the focus
4237      * @return {Ext.Element} this
4238      */
4239     focus : function(defer, /* private */ dom) {
4240         var me = this,
4241             dom = dom || me.dom;
4242         try{
4243             if(Number(defer)){
4244                 me.focus.defer(defer, null, [null, dom]);
4245             }else{
4246                 dom.focus();
4247             }
4248         }catch(e){}
4249         return me;
4250     },
4251
4252     /**
4253      * Tries to blur the element. Any exceptions are caught and ignored.
4254      * @return {Ext.Element} this
4255      */
4256     blur : function() {
4257         try{
4258             this.dom.blur();
4259         }catch(e){}
4260         return this;
4261     },
4262
4263     /**
4264      * Returns the value of the "value" attribute
4265      * @param {Boolean} asNumber true to parse the value as a number
4266      * @return {String/Number}
4267      */
4268     getValue : function(asNumber){
4269         var val = this.dom.value;
4270         return asNumber ? parseInt(val, 10) : val;
4271     },
4272
4273     /**
4274      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
4275      * @param {String} eventName The name of event to handle.
4276      * @param {Function} fn The handler function the event invokes. This function is passed
4277      * the following parameters:<ul>
4278      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4279      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4280      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4281      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4282      * </ul>
4283      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4284      * <b>If omitted, defaults to this Element.</b>.
4285      * @param {Object} options (optional) An object containing handler configuration properties.
4286      * This may contain any of the following properties:<ul>
4287      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4288      * <b>If omitted, defaults to this Element.</b></div></li>
4289      * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
4290      * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
4291      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4292      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4293      * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
4294      * <li><b>target</b> Ext.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
4295      * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
4296      * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
4297      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4298      * by the specified number of milliseconds. If the event fires again within that time, the original
4299      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4300      * </ul><br>
4301      * <p>
4302      * <b>Combining Options</b><br>
4303      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4304      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
4305      * types of listeners:<br>
4306      * <br>
4307      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4308      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4309      * Code:<pre><code>
4310 el.on('click', this.onClick, this, {
4311     single: true,
4312     delay: 100,
4313     stopEvent : true,
4314     forumId: 4
4315 });</code></pre></p>
4316      * <p>
4317      * <b>Attaching multiple handlers in 1 call</b><br>
4318      * The method also allows for a single argument to be passed which is a config object containing properties
4319      * which specify multiple handlers.</p>
4320      * <p>
4321      * Code:<pre><code>
4322 el.on({
4323     'click' : {
4324         fn: this.onClick,
4325         scope: this,
4326         delay: 100
4327     },
4328     'mouseover' : {
4329         fn: this.onMouseOver,
4330         scope: this
4331     },
4332     'mouseout' : {
4333         fn: this.onMouseOut,
4334         scope: this
4335     }
4336 });</code></pre>
4337      * <p>
4338      * Or a shorthand syntax:<br>
4339      * Code:<pre><code></p>
4340 el.on({
4341     'click' : this.onClick,
4342     'mouseover' : this.onMouseOver,
4343     'mouseout' : this.onMouseOut,
4344     scope: this
4345 });
4346      * </code></pre></p>
4347      * <p><b>delegate</b></p>
4348      * <p>This is a configuration option that you can pass along when registering a handler for
4349      * an event to assist with event delegation. Event delegation is a technique that is used to
4350      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4351      * for a container element as opposed to each element within a container. By setting this
4352      * configuration option to a simple selector, the target element will be filtered to look for
4353      * a descendant of the target.
4354      * For example:<pre><code>
4355 // using this markup:
4356 &lt;div id='elId'>
4357     &lt;p id='p1'>paragraph one&lt;/p>
4358     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
4359     &lt;p id='p3'>paragraph three&lt;/p>
4360 &lt;/div>
4361 // utilize event delegation to registering just one handler on the container element:
4362 el = Ext.get('elId');
4363 el.on(
4364     'click',
4365     function(e,t) {
4366         // handle click
4367         console.info(t.id); // 'p2'
4368     },
4369     this,
4370     {
4371         // filter the target element to be a descendant with the class 'clickable'
4372         delegate: '.clickable'
4373     }
4374 );
4375      * </code></pre></p>
4376      * @return {Ext.Element} this
4377      */
4378     addListener : function(eventName, fn, scope, options){
4379         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
4380         return this;
4381     },
4382
4383     /**
4384      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
4385      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4386      * listener, the same scope must be specified here.
4387      * Example:
4388      * <pre><code>
4389 el.removeListener('click', this.handlerFn);
4390 // or
4391 el.un('click', this.handlerFn);
4392 </code></pre>
4393      * @param {String} eventName The name of the event from which to remove the handler.
4394      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4395      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4396      * then this must refer to the same object.
4397      * @return {Ext.Element} this
4398      */
4399     removeListener : function(eventName, fn, scope){
4400         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
4401         return this;
4402     },
4403
4404     /**
4405      * Removes all previous added listeners from this element
4406      * @return {Ext.Element} this
4407      */
4408     removeAllListeners : function(){
4409         Ext.EventManager.removeAll(this.dom);
4410         return this;
4411     },
4412
4413     /**
4414      * Recursively removes all previous added listeners from this element and its children
4415      * @return {Ext.Element} this
4416      */
4417     purgeAllListeners : function() {
4418         Ext.EventManager.purgeElement(this, true);
4419         return this;
4420     },
4421     /**
4422      * @private Test if size has a unit, otherwise appends the default
4423      */
4424     addUnits : function(size){
4425         if(size === "" || size == "auto" || size === undefined){
4426             size = size || '';
4427         } else if(!isNaN(size) || !unitPattern.test(size)){
4428             size = size + (this.defaultUnit || 'px');
4429         }
4430         return size;
4431     },
4432
4433     /**
4434      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4435      * from a specified URL. Note that this is subject to the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">Same Origin Policy</a></p>
4436      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
4437      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4438      * exactly how to request the HTML.
4439      * @return {Ext.Element} this
4440      */
4441     load : function(url, params, cb){
4442         Ext.Ajax.request(Ext.apply({
4443             params: params,
4444             url: url.url || url,
4445             callback: cb,
4446             el: this.dom,
4447             indicatorText: url.indicatorText || ''
4448         }, Ext.isObject(url) ? url : {}));
4449         return this;
4450     },
4451
4452     /**
4453      * Tests various css rules/browsers to determine if this element uses a border box
4454      * @return {Boolean}
4455      */
4456     isBorderBox : function(){
4457         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4458     },
4459
4460     /**
4461      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4462      */
4463     remove : function(){
4464         var me = this,
4465             dom = me.dom;
4466
4467         if (dom) {
4468             delete me.dom;
4469             Ext.removeNode(dom);
4470         }
4471     },
4472
4473     /**
4474      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4475      * @param {Function} overFn The function to call when the mouse enters the Element.
4476      * @param {Function} outFn The function to call when the mouse leaves the Element.
4477      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4478      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4479      * @return {Ext.Element} this
4480      */
4481     hover : function(overFn, outFn, scope, options){
4482         var me = this;
4483         me.on('mouseenter', overFn, scope || me.dom, options);
4484         me.on('mouseleave', outFn, scope || me.dom, options);
4485         return me;
4486     },
4487
4488     /**
4489      * Returns true if this element is an ancestor of the passed element
4490      * @param {HTMLElement/String} el The element to check
4491      * @return {Boolean} True if this element is an ancestor of el, else false
4492      */
4493     contains : function(el){
4494         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4495     },
4496
4497     /**
4498      * Returns the value of a namespaced attribute from the element's underlying DOM node.
4499      * @param {String} namespace The namespace in which to look for the attribute
4500      * @param {String} name The attribute name
4501      * @return {String} The attribute value
4502      * @deprecated
4503      */
4504     getAttributeNS : function(ns, name){
4505         return this.getAttribute(name, ns);
4506     },
4507
4508     /**
4509      * Returns the value of an attribute from the element's underlying DOM node.
4510      * @param {String} name The attribute name
4511      * @param {String} namespace (optional) The namespace in which to look for the attribute
4512      * @return {String} The attribute value
4513      */
4514     getAttribute : Ext.isIE ? function(name, ns){
4515         var d = this.dom,
4516             type = typeof d[ns + ":" + name];
4517
4518         if(['undefined', 'unknown'].indexOf(type) == -1){
4519             return d[ns + ":" + name];
4520         }
4521         return d[name];
4522     } : function(name, ns){
4523         var d = this.dom;
4524         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4525     },
4526
4527     /**
4528     * Update the innerHTML of this element
4529     * @param {String} html The new HTML
4530     * @return {Ext.Element} this
4531      */
4532     update : function(html) {
4533         if (this.dom) {
4534             this.dom.innerHTML = html;
4535         }
4536         return this;
4537     }
4538 };
4539
4540 var ep = El.prototype;
4541
4542 El.addMethods = function(o){
4543    Ext.apply(ep, o);
4544 };
4545
4546 /**
4547  * Appends an event handler (shorthand for {@link #addListener}).
4548  * @param {String} eventName The name of event to handle.
4549  * @param {Function} fn The handler function the event invokes.
4550  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4551  * @param {Object} options (optional) An object containing standard {@link #addListener} options
4552  * @member Ext.Element
4553  * @method on
4554  */
4555 ep.on = ep.addListener;
4556
4557 /**
4558  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4559  * @param {String} eventName The name of the event from which to remove the handler.
4560  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4561  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4562  * then this must refer to the same object.
4563  * @return {Ext.Element} this
4564  * @member Ext.Element
4565  * @method un
4566  */
4567 ep.un = ep.removeListener;
4568
4569 /**
4570  * true to automatically adjust width and height settings for box-model issues (default to true)
4571  */
4572 ep.autoBoxAdjust = true;
4573
4574 // private
4575 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4576     docEl;
4577
4578 /**
4579  * @private
4580  */
4581
4582 /**
4583  * Retrieves Ext.Element objects.
4584  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4585  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4586  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4587  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4588  * object was recreated with the same id via AJAX or DOM.</p>
4589  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4590  * @return {Element} The Element object (or null if no matching element was found)
4591  * @static
4592  * @member Ext.Element
4593  * @method get
4594  */
4595 El.get = function(el){
4596     var ex,
4597         elm,
4598         id;
4599     if(!el){ return null; }
4600     if (typeof el == "string") { // element id
4601         if (!(elm = DOC.getElementById(el))) {
4602             return null;
4603         }
4604         if (EC[el] && EC[el].el) {
4605             ex = EC[el].el;
4606             ex.dom = elm;
4607         } else {
4608             ex = El.addToCache(new El(elm));
4609         }
4610         return ex;
4611     } else if (el.tagName) { // dom element
4612         if(!(id = el.id)){
4613             id = Ext.id(el);
4614         }
4615         if (EC[id] && EC[id].el) {
4616             ex = EC[id].el;
4617             ex.dom = el;
4618         } else {
4619             ex = El.addToCache(new El(el));
4620         }
4621         return ex;
4622     } else if (el instanceof El) {
4623         if(el != docEl){
4624             // refresh dom element in case no longer valid,
4625             // catch case where it hasn't been appended
4626
4627             // If an el instance is passed, don't pass to getElementById without some kind of id
4628             if (Ext.isIE && (el.id == undefined || el.id == '')) {
4629                 el.dom = el.dom;
4630             } else {
4631                 el.dom = DOC.getElementById(el.id) || el.dom;
4632             }
4633         }
4634         return el;
4635     } else if(el.isComposite) {
4636         return el;
4637     } else if(Ext.isArray(el)) {
4638         return El.select(el);
4639     } else if(el == DOC) {
4640         // create a bogus element object representing the document object
4641         if(!docEl){
4642             var f = function(){};
4643             f.prototype = El.prototype;
4644             docEl = new f();
4645             docEl.dom = DOC;
4646         }
4647         return docEl;
4648     }
4649     return null;
4650 };
4651
4652 El.addToCache = function(el, id){
4653     id = id || el.id;
4654     EC[id] = {
4655         el:  el,
4656         data: {},
4657         events: {}
4658     };
4659     return el;
4660 };
4661
4662 // private method for getting and setting element data
4663 El.data = function(el, key, value){
4664     el = El.get(el);
4665     if (!el) {
4666         return null;
4667     }
4668     var c = EC[el.id].data;
4669     if(arguments.length == 2){
4670         return c[key];
4671     }else{
4672         return (c[key] = value);
4673     }
4674 };
4675
4676 // private
4677 // Garbage collection - uncache elements/purge listeners on orphaned elements
4678 // so we don't hold a reference and cause the browser to retain them
4679 function garbageCollect(){
4680     if(!Ext.enableGarbageCollector){
4681         clearInterval(El.collectorThreadId);
4682     } else {
4683         var eid,
4684             el,
4685             d,
4686             o;
4687
4688         for(eid in EC){
4689             o = EC[eid];
4690             if(o.skipGC){
4691                 continue;
4692             }
4693             el = o.el;
4694             d = el.dom;
4695             // -------------------------------------------------------
4696             // Determining what is garbage:
4697             // -------------------------------------------------------
4698             // !d
4699             // dom node is null, definitely garbage
4700             // -------------------------------------------------------
4701             // !d.parentNode
4702             // no parentNode == direct orphan, definitely garbage
4703             // -------------------------------------------------------
4704             // !d.offsetParent && !document.getElementById(eid)
4705             // display none elements have no offsetParent so we will
4706             // also try to look it up by it's id. However, check
4707             // offsetParent first so we don't do unneeded lookups.
4708             // This enables collection of elements that are not orphans
4709             // directly, but somewhere up the line they have an orphan
4710             // parent.
4711             // -------------------------------------------------------
4712             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4713                 if(Ext.enableListenerCollection){
4714                     Ext.EventManager.removeAll(d);
4715                 }
4716                 delete EC[eid];
4717             }
4718         }
4719         // Cleanup IE Object leaks
4720         if (Ext.isIE) {
4721             var t = {};
4722             for (eid in EC) {
4723                 t[eid] = EC[eid];
4724             }
4725             EC = Ext.elCache = t;
4726         }
4727     }
4728 }
4729 El.collectorThreadId = setInterval(garbageCollect, 30000);
4730
4731 var flyFn = function(){};
4732 flyFn.prototype = El.prototype;
4733
4734 // dom is optional
4735 El.Flyweight = function(dom){
4736     this.dom = dom;
4737 };
4738
4739 El.Flyweight.prototype = new flyFn();
4740 El.Flyweight.prototype.isFlyweight = true;
4741 El._flyweights = {};
4742
4743 /**
4744  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4745  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4746  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4747  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4748  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4749  * @param {String/HTMLElement} el The dom node or id
4750  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4751  * (e.g. internally Ext uses "_global")
4752  * @return {Element} The shared Element object (or null if no matching element was found)
4753  * @member Ext.Element
4754  * @method fly
4755  */
4756 El.fly = function(el, named){
4757     var ret = null;
4758     named = named || '_global';
4759
4760     if (el = Ext.getDom(el)) {
4761         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4762         ret = El._flyweights[named];
4763     }
4764     return ret;
4765 };
4766
4767 /**
4768  * Retrieves Ext.Element objects.
4769  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4770  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4771  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4772  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4773  * object was recreated with the same id via AJAX or DOM.</p>
4774  * Shorthand of {@link Ext.Element#get}
4775  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4776  * @return {Element} The Element object (or null if no matching element was found)
4777  * @member Ext
4778  * @method get
4779  */
4780 Ext.get = El.get;
4781
4782 /**
4783  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4784  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4785  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4786  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4787  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4788  * @param {String/HTMLElement} el The dom node or id
4789  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4790  * (e.g. internally Ext uses "_global")
4791  * @return {Element} The shared Element object (or null if no matching element was found)
4792  * @member Ext
4793  * @method fly
4794  */
4795 Ext.fly = El.fly;
4796
4797 // speedy lookup for elements never to box adjust
4798 var noBoxAdjust = Ext.isStrict ? {
4799     select:1
4800 } : {
4801     input:1, select:1, textarea:1
4802 };
4803 if(Ext.isIE || Ext.isGecko){
4804     noBoxAdjust['button'] = 1;
4805 }
4806
4807 })();
4808 /**
4809  * @class Ext.Element
4810  */
4811 Ext.Element.addMethods({
4812     /**
4813      * Stops the specified event(s) from bubbling and optionally prevents the default action
4814      * @param {String/Array} eventName an event / array of events to stop from bubbling
4815      * @param {Boolean} preventDefault (optional) true to prevent the default action too
4816      * @return {Ext.Element} this
4817      */
4818     swallowEvent : function(eventName, preventDefault){
4819         var me = this;
4820         function fn(e){
4821             e.stopPropagation();
4822             if(preventDefault){
4823                 e.preventDefault();
4824             }
4825         }
4826         if(Ext.isArray(eventName)){
4827             Ext.each(eventName, function(e) {
4828                  me.on(e, fn);
4829             });
4830             return me;
4831         }
4832         me.on(eventName, fn);
4833         return me;
4834     },
4835
4836     /**
4837      * Create an event handler on this element such that when the event fires and is handled by this element,
4838      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4839      * @param {String} eventName The type of event to relay
4840      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4841      * for firing the relayed event
4842      */
4843     relayEvent : function(eventName, observable){
4844         this.on(eventName, function(e){
4845             observable.fireEvent(eventName, e);
4846         });
4847     },
4848
4849     /**
4850      * Removes worthless text nodes
4851      * @param {Boolean} forceReclean (optional) By default the element
4852      * keeps track if it has been cleaned already so
4853      * you can call this over and over. However, if you update the element and
4854      * need to force a reclean, you can pass true.
4855      */
4856     clean : function(forceReclean){
4857         var me = this,
4858             dom = me.dom,
4859             n = dom.firstChild,
4860             ni = -1;
4861
4862         if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4863             return me;
4864         }
4865
4866         while(n){
4867             var nx = n.nextSibling;
4868             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4869                 dom.removeChild(n);
4870             }else{
4871                 n.nodeIndex = ++ni;
4872             }
4873             n = nx;
4874         }
4875         Ext.Element.data(dom, 'isCleaned', true);
4876         return me;
4877     },
4878
4879     /**
4880      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4881      * parameter as {@link Ext.Updater#update}
4882      * @return {Ext.Element} this
4883      */
4884     load : function(){
4885         var um = this.getUpdater();
4886         um.update.apply(um, arguments);
4887         return this;
4888     },
4889
4890     /**
4891     * Gets this element's {@link Ext.Updater Updater}
4892     * @return {Ext.Updater} The Updater
4893     */
4894     getUpdater : function(){
4895         return this.updateManager || (this.updateManager = new Ext.Updater(this));
4896     },
4897
4898     /**
4899     * Update the innerHTML of this element, optionally searching for and processing scripts
4900     * @param {String} html The new HTML
4901     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4902     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4903     * @return {Ext.Element} this
4904      */
4905     update : function(html, loadScripts, callback){
4906         if (!this.dom) {
4907             return this;
4908         }
4909         html = html || "";
4910
4911         if(loadScripts !== true){
4912             this.dom.innerHTML = html;
4913             if(Ext.isFunction(callback)){
4914                 callback();
4915             }
4916             return this;
4917         }
4918
4919         var id = Ext.id(),
4920             dom = this.dom;
4921
4922         html += '<span id="' + id + '"></span>';
4923
4924         Ext.lib.Event.onAvailable(id, function(){
4925             var DOC = document,
4926                 hd = DOC.getElementsByTagName("head")[0],
4927                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4928                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4929                 typeRe = /\stype=([\'\"])(.*?)\1/i,
4930                 match,
4931                 attrs,
4932                 srcMatch,
4933                 typeMatch,
4934                 el,
4935                 s;
4936
4937             while((match = re.exec(html))){
4938                 attrs = match[1];
4939                 srcMatch = attrs ? attrs.match(srcRe) : false;
4940                 if(srcMatch && srcMatch[2]){
4941                    s = DOC.createElement("script");
4942                    s.src = srcMatch[2];
4943                    typeMatch = attrs.match(typeRe);
4944                    if(typeMatch && typeMatch[2]){
4945                        s.type = typeMatch[2];
4946                    }
4947                    hd.appendChild(s);
4948                 }else if(match[2] && match[2].length > 0){
4949                     if(window.execScript) {
4950                        window.execScript(match[2]);
4951                     } else {
4952                        window.eval(match[2]);
4953                     }
4954                 }
4955             }
4956             el = DOC.getElementById(id);
4957             if(el){Ext.removeNode(el);}
4958             if(Ext.isFunction(callback)){
4959                 callback();
4960             }
4961         });
4962         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4963         return this;
4964     },
4965
4966     // inherit docs, overridden so we can add removeAnchor
4967     removeAllListeners : function(){
4968         this.removeAnchor();
4969         Ext.EventManager.removeAll(this.dom);
4970         return this;
4971     },
4972
4973     /**
4974      * Creates a proxy element of this element
4975      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
4976      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
4977      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
4978      * @return {Ext.Element} The new proxy element
4979      */
4980     createProxy : function(config, renderTo, matchBox){
4981         config = Ext.isObject(config) ? config : {tag : "div", cls: config};
4982
4983         var me = this,
4984             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
4985                                Ext.DomHelper.insertBefore(me.dom, config, true);
4986
4987         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
4988            proxy.setBox(me.getBox());
4989         }
4990         return proxy;
4991     }
4992 });
4993
4994 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
4995 /**
4996  * @class Ext.Element
4997  */
4998 Ext.Element.addMethods({
4999     /**
5000      * Gets the x,y coordinates specified by the anchor position on the element.
5001      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
5002      * for details on supported anchor positions.
5003      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5004      * of page coordinates
5005      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5006      * {width: (target width), height: (target height)} (defaults to the element's current size)
5007      * @return {Array} [x, y] An array containing the element's x and y coordinates
5008      */
5009     getAnchorXY : function(anchor, local, s){
5010         //Passing a different size is useful for pre-calculating anchors,
5011         //especially for anchored animations that change the el size.
5012                 anchor = (anchor || "tl").toLowerCase();
5013         s = s || {};
5014         
5015         var me = this,        
5016                 vp = me.dom == document.body || me.dom == document,
5017                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5018                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
5019                 xy,             
5020                 r = Math.round,
5021                 o = me.getXY(),
5022                 scroll = me.getScroll(),
5023                 extraX = vp ? scroll.left : !local ? o[0] : 0,
5024                 extraY = vp ? scroll.top : !local ? o[1] : 0,
5025                 hash = {
5026                         c  : [r(w * 0.5), r(h * 0.5)],
5027                         t  : [r(w * 0.5), 0],
5028                         l  : [0, r(h * 0.5)],
5029                         r  : [w, r(h * 0.5)],
5030                         b  : [r(w * 0.5), h],
5031                         tl : [0, 0],    
5032                         bl : [0, h],
5033                         br : [w, h],
5034                         tr : [w, 0]
5035                 };
5036         
5037         xy = hash[anchor];      
5038         return [xy[0] + extraX, xy[1] + extraY]; 
5039     },
5040
5041     /**
5042      * Anchors an element to another element and realigns it when the window is resized.
5043      * @param {Mixed} element The element to align to.
5044      * @param {String} position The position to align to.
5045      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5046      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5047      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5048      * is a number, it is used as the buffer delay (defaults to 50ms).
5049      * @param {Function} callback The function to call after the animation finishes
5050      * @return {Ext.Element} this
5051      */
5052     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
5053             var me = this,
5054             dom = me.dom,
5055             scroll = !Ext.isEmpty(monitorScroll),
5056             action = function(){
5057                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5058                 Ext.callback(callback, Ext.fly(dom));
5059             },
5060             anchor = this.getAnchor();
5061             
5062         // previous listener anchor, remove it
5063         this.removeAnchor();
5064         Ext.apply(anchor, {
5065             fn: action,
5066             scroll: scroll
5067         });
5068
5069         Ext.EventManager.onWindowResize(action, null);
5070         
5071         if(scroll){
5072             Ext.EventManager.on(window, 'scroll', action, null,
5073                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5074         }
5075         action.call(me); // align immediately
5076         return me;
5077     },
5078     
5079     /**
5080      * Remove any anchor to this element. See {@link #anchorTo}.
5081      * @return {Ext.Element} this
5082      */
5083     removeAnchor : function(){
5084         var me = this,
5085             anchor = this.getAnchor();
5086             
5087         if(anchor && anchor.fn){
5088             Ext.EventManager.removeResizeListener(anchor.fn);
5089             if(anchor.scroll){
5090                 Ext.EventManager.un(window, 'scroll', anchor.fn);
5091             }
5092             delete anchor.fn;
5093         }
5094         return me;
5095     },
5096     
5097     // private
5098     getAnchor : function(){
5099         var data = Ext.Element.data,
5100             dom = this.dom;
5101             if (!dom) {
5102                 return;
5103             }
5104             var anchor = data(dom, '_anchor');
5105             
5106         if(!anchor){
5107             anchor = data(dom, '_anchor', {});
5108         }
5109         return anchor;
5110     },
5111
5112     /**
5113      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5114      * supported position values.
5115      * @param {Mixed} element The element to align to.
5116      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5117      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5118      * @return {Array} [x, y]
5119      */
5120     getAlignToXY : function(el, p, o){      
5121         el = Ext.get(el);
5122         
5123         if(!el || !el.dom){
5124             throw "Element.alignToXY with an element that doesn't exist";
5125         }
5126         
5127         o = o || [0,0];
5128         p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
5129                 
5130         var me = this,
5131                 d = me.dom,
5132                 a1,
5133                 a2,
5134                 x,
5135                 y,
5136                 //constrain the aligned el to viewport if necessary
5137                 w,
5138                 h,
5139                 r,
5140                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5141                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5142                 p1y,
5143                 p1x,            
5144                 p2y,
5145                 p2x,
5146                 swapY,
5147                 swapX,
5148                 doc = document,
5149                 docElement = doc.documentElement,
5150                 docBody = doc.body,
5151                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5152                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5153                 c = false, //constrain to viewport
5154                 p1 = "", 
5155                 p2 = "",
5156                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5157         
5158         if(!m){
5159            throw "Element.alignTo with an invalid alignment " + p;
5160         }
5161         
5162         p1 = m[1]; 
5163         p2 = m[2]; 
5164         c = !!m[3];
5165
5166         //Subtract the aligned el's internal xy from the target's offset xy
5167         //plus custom offset to get the aligned el's new offset xy
5168         a1 = me.getAnchorXY(p1, true);
5169         a2 = el.getAnchorXY(p2, false);
5170
5171         x = a2[0] - a1[0] + o[0];
5172         y = a2[1] - a1[1] + o[1];
5173
5174         if(c){    
5175                w = me.getWidth();
5176            h = me.getHeight();
5177            r = el.getRegion();       
5178            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5179            //perpendicular to the vp border, allow the aligned el to slide on that border,
5180            //otherwise swap the aligned el to the opposite border of the target.
5181            p1y = p1.charAt(0);
5182            p1x = p1.charAt(p1.length-1);
5183            p2y = p2.charAt(0);
5184            p2x = p2.charAt(p2.length-1);
5185            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5186            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
5187            
5188
5189            if (x + w > dw + scrollX) {
5190                 x = swapX ? r.left-w : dw+scrollX-w;
5191            }
5192            if (x < scrollX) {
5193                x = swapX ? r.right : scrollX;
5194            }
5195            if (y + h > dh + scrollY) {
5196                 y = swapY ? r.top-h : dh+scrollY-h;
5197             }
5198            if (y < scrollY){
5199                y = swapY ? r.bottom : scrollY;
5200            }
5201         }
5202         return [x,y];
5203     },
5204
5205     /**
5206      * Aligns this element with another element relative to the specified anchor points. If the other element is the
5207      * document it aligns it to the viewport.
5208      * The position parameter is optional, and can be specified in any one of the following formats:
5209      * <ul>
5210      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5211      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5212      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
5213      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
5214      *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
5215      *       element's anchor point, and the second value is used as the target's anchor point.</li>
5216      * </ul>
5217      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
5218      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5219      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
5220      * that specified in order to enforce the viewport constraints.
5221      * Following are all of the supported anchor positions:
5222 <pre>
5223 Value  Description
5224 -----  -----------------------------
5225 tl     The top left corner (default)
5226 t      The center of the top edge
5227 tr     The top right corner
5228 l      The center of the left edge
5229 c      In the center of the element
5230 r      The center of the right edge
5231 bl     The bottom left corner
5232 b      The center of the bottom edge
5233 br     The bottom right corner
5234 </pre>
5235 Example Usage:
5236 <pre><code>
5237 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5238 el.alignTo("other-el");
5239
5240 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5241 el.alignTo("other-el", "tr?");
5242
5243 // align the bottom right corner of el with the center left edge of other-el
5244 el.alignTo("other-el", "br-l?");
5245
5246 // align the center of el with the bottom left corner of other-el and
5247 // adjust the x position by -6 pixels (and the y position by 0)
5248 el.alignTo("other-el", "c-bl", [-6, 0]);
5249 </code></pre>
5250      * @param {Mixed} element The element to align to.
5251      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5252      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5253      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5254      * @return {Ext.Element} this
5255      */
5256     alignTo : function(element, position, offsets, animate){
5257             var me = this;
5258         return me.setXY(me.getAlignToXY(element, position, offsets),
5259                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5260     },
5261     
5262     // private ==>  used outside of core
5263     adjustForConstraints : function(xy, parent, offsets){
5264         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
5265     },
5266
5267     // private ==>  used outside of core
5268     getConstrainToXY : function(el, local, offsets, proposedXY){   
5269             var os = {top:0, left:0, bottom:0, right: 0};
5270
5271         return function(el, local, offsets, proposedXY){
5272             el = Ext.get(el);
5273             offsets = offsets ? Ext.applyIf(offsets, os) : os;
5274
5275             var vw, vh, vx = 0, vy = 0;
5276             if(el.dom == document.body || el.dom == document){
5277                 vw =Ext.lib.Dom.getViewWidth();
5278                 vh = Ext.lib.Dom.getViewHeight();
5279             }else{
5280                 vw = el.dom.clientWidth;
5281                 vh = el.dom.clientHeight;
5282                 if(!local){
5283                     var vxy = el.getXY();
5284                     vx = vxy[0];
5285                     vy = vxy[1];
5286                 }
5287             }
5288
5289             var s = el.getScroll();
5290
5291             vx += offsets.left + s.left;
5292             vy += offsets.top + s.top;
5293
5294             vw -= offsets.right;
5295             vh -= offsets.bottom;
5296
5297             var vr = vx+vw;
5298             var vb = vy+vh;
5299
5300             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5301             var x = xy[0], y = xy[1];
5302             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5303
5304             // only move it if it needs it
5305             var moved = false;
5306
5307             // first validate right/bottom
5308             if((x + w) > vr){
5309                 x = vr - w;
5310                 moved = true;
5311             }
5312             if((y + h) > vb){
5313                 y = vb - h;
5314                 moved = true;
5315             }
5316             // then make sure top/left isn't negative
5317             if(x < vx){
5318                 x = vx;
5319                 moved = true;
5320             }
5321             if(y < vy){
5322                 y = vy;
5323                 moved = true;
5324             }
5325             return moved ? [x, y] : false;
5326         };
5327     }(),
5328             
5329             
5330                 
5331 //         el = Ext.get(el);
5332 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5333
5334 //         var  me = this,
5335 //              doc = document,
5336 //              s = el.getScroll(),
5337 //              vxy = el.getXY(),
5338 //              vx = offsets.left + s.left, 
5339 //              vy = offsets.top + s.top,               
5340 //              vw = -offsets.right, 
5341 //              vh = -offsets.bottom, 
5342 //              vr,
5343 //              vb,
5344 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5345 //              x = xy[0],
5346 //              y = xy[1],
5347 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5348 //              moved = false; // only move it if it needs it
5349 //       
5350 //              
5351 //         if(el.dom == doc.body || el.dom == doc){
5352 //             vw += Ext.lib.Dom.getViewWidth();
5353 //             vh += Ext.lib.Dom.getViewHeight();
5354 //         }else{
5355 //             vw += el.dom.clientWidth;
5356 //             vh += el.dom.clientHeight;
5357 //             if(!local){                    
5358 //                 vx += vxy[0];
5359 //                 vy += vxy[1];
5360 //             }
5361 //         }
5362
5363 //         // first validate right/bottom
5364 //         if(x + w > vx + vw){
5365 //             x = vx + vw - w;
5366 //             moved = true;
5367 //         }
5368 //         if(y + h > vy + vh){
5369 //             y = vy + vh - h;
5370 //             moved = true;
5371 //         }
5372 //         // then make sure top/left isn't negative
5373 //         if(x < vx){
5374 //             x = vx;
5375 //             moved = true;
5376 //         }
5377 //         if(y < vy){
5378 //             y = vy;
5379 //             moved = true;
5380 //         }
5381 //         return moved ? [x, y] : false;
5382 //    },
5383     
5384     /**
5385     * Calculates the x, y to center this element on the screen
5386     * @return {Array} The x, y values [x, y]
5387     */
5388     getCenterXY : function(){
5389         return this.getAlignToXY(document, 'c-c');
5390     },
5391
5392     /**
5393     * Centers the Element in either the viewport, or another Element.
5394     * @param {Mixed} centerIn (optional) The element in which to center the element.
5395     */
5396     center : function(centerIn){
5397         return this.alignTo(centerIn || document, 'c-c');        
5398     }    
5399 });
5400 /**
5401  * @class Ext.Element
5402  */
5403 Ext.Element.addMethods(function(){
5404         var PARENTNODE = 'parentNode',
5405                 NEXTSIBLING = 'nextSibling',
5406                 PREVIOUSSIBLING = 'previousSibling',
5407                 DQ = Ext.DomQuery,
5408                 GET = Ext.get;
5409         
5410         return {
5411                 /**
5412              * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5413              * @param {String} selector The simple selector to test
5414              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5415              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5416              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5417              */
5418             findParent : function(simpleSelector, maxDepth, returnEl){
5419                 var p = this.dom,
5420                         b = document.body, 
5421                         depth = 0,                      
5422                         stopEl;         
5423             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5424                 return null;
5425             }
5426                 maxDepth = maxDepth || 50;
5427                 if (isNaN(maxDepth)) {
5428                     stopEl = Ext.getDom(maxDepth);
5429                     maxDepth = Number.MAX_VALUE;
5430                 }
5431                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5432                     if(DQ.is(p, simpleSelector)){
5433                         return returnEl ? GET(p) : p;
5434                     }
5435                     depth++;
5436                     p = p.parentNode;
5437                 }
5438                 return null;
5439             },
5440         
5441             /**
5442              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5443              * @param {String} selector The simple selector to test
5444              * @param {Number/Mixed} maxDepth (optional) The max depth to
5445                     search as a number or element (defaults to 10 || document.body)
5446              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5447              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5448              */
5449             findParentNode : function(simpleSelector, maxDepth, returnEl){
5450                 var p = Ext.fly(this.dom.parentNode, '_internal');
5451                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5452             },
5453         
5454             /**
5455              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5456              * This is a shortcut for findParentNode() that always returns an Ext.Element.
5457              * @param {String} selector The simple selector to test
5458              * @param {Number/Mixed} maxDepth (optional) The max depth to
5459                     search as a number or element (defaults to 10 || document.body)
5460              * @return {Ext.Element} The matching DOM node (or null if no match was found)
5461              */
5462             up : function(simpleSelector, maxDepth){
5463                 return this.findParentNode(simpleSelector, maxDepth, true);
5464             },
5465         
5466             /**
5467              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5468              * @param {String} selector The CSS selector
5469              * @return {CompositeElement/CompositeElementLite} The composite element
5470              */
5471             select : function(selector){
5472                 return Ext.Element.select(selector, this.dom);
5473             },
5474         
5475             /**
5476              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5477              * @param {String} selector The CSS selector
5478              * @return {Array} An array of the matched nodes
5479              */
5480             query : function(selector){
5481                 return DQ.select(selector, this.dom);
5482             },
5483         
5484             /**
5485              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5486              * @param {String} selector The CSS selector
5487              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5488              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5489              */
5490             child : function(selector, returnDom){
5491                 var n = DQ.selectNode(selector, this.dom);
5492                 return returnDom ? n : GET(n);
5493             },
5494         
5495             /**
5496              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5497              * @param {String} selector The CSS selector
5498              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5499              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5500              */
5501             down : function(selector, returnDom){
5502                 var n = DQ.selectNode(" > " + selector, this.dom);
5503                 return returnDom ? n : GET(n);
5504             },
5505         
5506                  /**
5507              * Gets the parent node for this element, optionally chaining up trying to match a selector
5508              * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5509              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5510              * @return {Ext.Element/HTMLElement} The parent node or null
5511                  */
5512             parent : function(selector, returnDom){
5513                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5514             },
5515         
5516              /**
5517              * Gets the next sibling, skipping text nodes
5518              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5519              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5520              * @return {Ext.Element/HTMLElement} The next sibling or null
5521                  */
5522             next : function(selector, returnDom){
5523                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5524             },
5525         
5526             /**
5527              * Gets the previous sibling, skipping text nodes
5528              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5529              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5530              * @return {Ext.Element/HTMLElement} The previous sibling or null
5531                  */
5532             prev : function(selector, returnDom){
5533                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5534             },
5535         
5536         
5537             /**
5538              * Gets the first child, skipping text nodes
5539              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5540              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5541              * @return {Ext.Element/HTMLElement} The first child or null
5542                  */
5543             first : function(selector, returnDom){
5544                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5545             },
5546         
5547             /**
5548              * Gets the last child, skipping text nodes
5549              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5550              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5551              * @return {Ext.Element/HTMLElement} The last child or null
5552                  */
5553             last : function(selector, returnDom){
5554                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5555             },
5556             
5557             matchNode : function(dir, start, selector, returnDom){
5558                 var n = this.dom[start];
5559                 while(n){
5560                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5561                         return !returnDom ? GET(n) : n;
5562                     }
5563                     n = n[dir];
5564                 }
5565                 return null;
5566             }   
5567     }
5568 }());/**
5569  * @class Ext.Element
5570  */
5571 Ext.Element.addMethods({
5572     /**
5573      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5574      * @param {String} selector The CSS selector
5575      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5576      * @return {CompositeElement/CompositeElementLite} The composite element
5577      */
5578     select : function(selector, unique){
5579         return Ext.Element.select(selector, unique, this.dom);
5580     }
5581 });/**
5582  * @class Ext.Element
5583  */
5584 Ext.Element.addMethods(
5585 function() {
5586         var GETDOM = Ext.getDom,
5587                 GET = Ext.get,
5588                 DH = Ext.DomHelper;
5589         
5590         return {
5591             /**
5592              * Appends the passed element(s) to this element
5593              * @param {String/HTMLElement/Array/Element/CompositeElement} el
5594              * @return {Ext.Element} this
5595              */
5596             appendChild: function(el){        
5597                 return GET(el).appendTo(this);        
5598             },
5599         
5600             /**
5601              * Appends this element to the passed element
5602              * @param {Mixed} el The new parent element
5603              * @return {Ext.Element} this
5604              */
5605             appendTo: function(el){        
5606                 GETDOM(el).appendChild(this.dom);        
5607                 return this;
5608             },
5609         
5610             /**
5611              * Inserts this element before the passed element in the DOM
5612              * @param {Mixed} el The element before which this element will be inserted
5613              * @return {Ext.Element} this
5614              */
5615             insertBefore: function(el){                   
5616                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5617                 return this;
5618             },
5619         
5620             /**
5621              * Inserts this element after the passed element in the DOM
5622              * @param {Mixed} el The element to insert after
5623              * @return {Ext.Element} this
5624              */
5625             insertAfter: function(el){
5626                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5627                 return this;
5628             },
5629         
5630             /**
5631              * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5632              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5633              * @return {Ext.Element} The new child
5634              */
5635             insertFirst: function(el, returnDom){
5636             el = el || {};
5637             if(el.nodeType || el.dom || typeof el == 'string'){ // element
5638                 el = GETDOM(el);
5639                 this.dom.insertBefore(el, this.dom.firstChild);
5640                 return !returnDom ? GET(el) : el;
5641             }else{ // dh config
5642                 return this.createChild(el, this.dom.firstChild, returnDom);
5643             }
5644         },
5645         
5646             /**
5647              * Replaces the passed element with this element
5648              * @param {Mixed} el The element to replace
5649              * @return {Ext.Element} this
5650              */
5651             replace: function(el){
5652                 el = GET(el);
5653                 this.insertBefore(el);
5654                 el.remove();
5655                 return this;
5656             },
5657         
5658             /**
5659              * Replaces this element with the passed element
5660              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5661              * @return {Ext.Element} this
5662              */
5663             replaceWith: function(el){
5664                     var me = this;
5665                 
5666             if(el.nodeType || el.dom || typeof el == 'string'){
5667                 el = GETDOM(el);
5668                 me.dom.parentNode.insertBefore(el, me.dom);
5669             }else{
5670                 el = DH.insertBefore(me.dom, el);
5671             }
5672                 
5673                 delete Ext.elCache[me.id];
5674                 Ext.removeNode(me.dom);      
5675                 me.id = Ext.id(me.dom = el);
5676                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
5677             return me;
5678             },
5679             
5680                 /**
5681                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5682                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
5683                  * automatically generated with the specified attributes.
5684                  * @param {HTMLElement} insertBefore (optional) a child element of this element
5685                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5686                  * @return {Ext.Element} The new child element
5687                  */
5688                 createChild: function(config, insertBefore, returnDom){
5689                     config = config || {tag:'div'};
5690                     return insertBefore ? 
5691                            DH.insertBefore(insertBefore, config, returnDom !== true) :  
5692                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
5693                 },
5694                 
5695                 /**
5696                  * Creates and wraps this element with another element
5697                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5698                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5699                  * @return {HTMLElement/Element} The newly created wrapper element
5700                  */
5701                 wrap: function(config, returnDom){        
5702                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5703                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5704                     return newEl;
5705                 },
5706                 
5707                 /**
5708                  * Inserts an html fragment into this element
5709                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5710                  * @param {String} html The HTML fragment
5711                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5712                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5713                  */
5714                 insertHtml : function(where, html, returnEl){
5715                     var el = DH.insertHtml(where, this.dom, html);
5716                     return returnEl ? Ext.get(el) : el;
5717                 }
5718         }
5719 }());/**
5720  * @class Ext.Element
5721  */
5722 Ext.apply(Ext.Element.prototype, function() {
5723         var GETDOM = Ext.getDom,
5724                 GET = Ext.get,
5725                 DH = Ext.DomHelper;
5726         
5727         return {        
5728                 /**
5729              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5730              * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.
5731              * @param {String} where (optional) 'before' or 'after' defaults to before
5732              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5733              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5734              */
5735             insertSibling: function(el, where, returnDom){
5736                 var me = this,
5737                         rt,
5738                 isAfter = (where || 'before').toLowerCase() == 'after',
5739                 insertEl;
5740                         
5741                 if(Ext.isArray(el)){
5742                 insertEl = me;
5743                     Ext.each(el, function(e) {
5744                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5745                     if(isAfter){
5746                         insertEl = rt;
5747                     }
5748                     });
5749                     return rt;
5750                 }
5751                         
5752                 el = el || {};
5753                 
5754             if(el.nodeType || el.dom){
5755                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5756                 if (!returnDom) {
5757                     rt = GET(rt);
5758                 }
5759             }else{
5760                 if (isAfter && !me.dom.nextSibling) {
5761                     rt = DH.append(me.dom.parentNode, el, !returnDom);
5762                 } else {                    
5763                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5764                 }
5765             }
5766                 return rt;
5767             }
5768     };
5769 }());/**
5770  * @class Ext.Element
5771  */
5772 Ext.Element.addMethods(function(){
5773     // local style camelizing for speed
5774     var propCache = {},
5775         camelRe = /(-[a-z])/gi,
5776         classReCache = {},
5777         view = document.defaultView,
5778         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5779         opacityRe = /alpha\(opacity=(.*)\)/i,
5780         trimRe = /^\s+|\s+$/g,
5781         EL = Ext.Element,
5782         PADDING = "padding",
5783         MARGIN = "margin",
5784         BORDER = "border",
5785         LEFT = "-left",
5786         RIGHT = "-right",
5787         TOP = "-top",
5788         BOTTOM = "-bottom",
5789         WIDTH = "-width",
5790         MATH = Math,
5791         HIDDEN = 'hidden',
5792         ISCLIPPED = 'isClipped',
5793         OVERFLOW = 'overflow',
5794         OVERFLOWX = 'overflow-x',
5795         OVERFLOWY = 'overflow-y',
5796         ORIGINALCLIP = 'originalClip',
5797         // special markup used throughout Ext when box wrapping elements
5798         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5799         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5800         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5801         data = Ext.Element.data;
5802
5803
5804     // private
5805     function camelFn(m, a) {
5806         return a.charAt(1).toUpperCase();
5807     }
5808
5809     function chkCache(prop) {
5810         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5811     }
5812
5813     return {
5814         // private  ==> used by Fx
5815         adjustWidth : function(width) {
5816             var me = this;
5817             var isNum = Ext.isNumber(width);
5818             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5819                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5820             }
5821             return (isNum && width < 0) ? 0 : width;
5822         },
5823
5824         // private   ==> used by Fx
5825         adjustHeight : function(height) {
5826             var me = this;
5827             var isNum = Ext.isNumber(height);
5828             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5829                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5830             }
5831             return (isNum && height < 0) ? 0 : height;
5832         },
5833
5834
5835         /**
5836          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5837          * @param {String/Array} className The CSS class to add, or an array of classes
5838          * @return {Ext.Element} this
5839          */
5840         addClass : function(className){
5841             var me = this, i, len, v;
5842             className = Ext.isArray(className) ? className : [className];
5843             for (i=0, len = className.length; i < len; i++) {
5844                 v = className[i];
5845                 if (v) {
5846                     me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
5847                 };
5848             };
5849             return me;
5850         },
5851
5852         /**
5853          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5854          * @param {String/Array} className The CSS class to add, or an array of classes
5855          * @return {Ext.Element} this
5856          */
5857         radioClass : function(className){
5858             var cn = this.dom.parentNode.childNodes, v;
5859             className = Ext.isArray(className) ? className : [className];
5860             for (var i=0, len = cn.length; i < len; i++) {
5861                 v = cn[i];
5862                 if(v && v.nodeType == 1) {
5863                     Ext.fly(v, '_internal').removeClass(className);
5864                 }
5865             };
5866             return this.addClass(className);
5867         },
5868
5869         /**
5870          * Removes one or more CSS classes from the element.
5871          * @param {String/Array} className The CSS class to remove, or an array of classes
5872          * @return {Ext.Element} this
5873          */
5874         removeClass : function(className){
5875             var me = this, v;
5876             className = Ext.isArray(className) ? className : [className];
5877             if (me.dom && me.dom.className) {
5878                 for (var i=0, len=className.length; i < len; i++) {
5879                     v = className[i];
5880                     if(v) {
5881                         me.dom.className = me.dom.className.replace(
5882                             classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), " "
5883                         );
5884                     }
5885                 };
5886             }
5887             return me;
5888         },
5889
5890         /**
5891          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5892          * @param {String} className The CSS class to toggle
5893          * @return {Ext.Element} this
5894          */
5895         toggleClass : function(className){
5896             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5897         },
5898
5899         /**
5900          * Checks if the specified CSS class exists on this element's DOM node.
5901          * @param {String} className The CSS class to check for
5902          * @return {Boolean} True if the class exists, else false
5903          */
5904         hasClass : function(className){
5905             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5906         },
5907
5908         /**
5909          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5910          * @param {String} oldClassName The CSS class to replace
5911          * @param {String} newClassName The replacement CSS class
5912          * @return {Ext.Element} this
5913          */
5914         replaceClass : function(oldClassName, newClassName){
5915             return this.removeClass(oldClassName).addClass(newClassName);
5916         },
5917
5918         isStyle : function(style, val) {
5919             return this.getStyle(style) == val;
5920         },
5921
5922         /**
5923          * Normalizes currentStyle and computedStyle.
5924          * @param {String} property The style property whose value is returned.
5925          * @return {String} The current value of the style property for this element.
5926          */
5927         getStyle : function(){
5928             return view && view.getComputedStyle ?
5929                 function(prop){
5930                     var el = this.dom,
5931                         v,
5932                         cs,
5933                         out,
5934                         display,
5935                         wk = Ext.isWebKit,
5936                         display;
5937                         
5938                     if(el == document){
5939                         return null;
5940                     }
5941                     prop = chkCache(prop);
5942                     // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
5943                     if(wk && /marginRight/.test(prop)){
5944                         display = this.getStyle('display');
5945                         el.style.display = 'inline-block';
5946                     }
5947                     out = (v = el.style[prop]) ? v :
5948                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5949
5950                     // Webkit returns rgb values for transparent.
5951                     if(wk){
5952                         if(out == 'rgba(0, 0, 0, 0)'){
5953                             out = 'transparent';
5954                         }else if(display){
5955                             el.style.display = display;
5956                         }
5957                     }
5958                     return out;
5959                 } :
5960                 function(prop){
5961                     var el = this.dom,
5962                         m,
5963                         cs;
5964
5965                     if(el == document) return null;
5966                     if (prop == 'opacity') {
5967                         if (el.style.filter.match) {
5968                             if(m = el.style.filter.match(opacityRe)){
5969                                 var fv = parseFloat(m[1]);
5970                                 if(!isNaN(fv)){
5971                                     return fv ? fv / 100 : 0;
5972                                 }
5973                             }
5974                         }
5975                         return 1;
5976                     }
5977                     prop = chkCache(prop);
5978                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5979                 };
5980         }(),
5981
5982         /**
5983          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5984          * are convert to standard 6 digit hex color.
5985          * @param {String} attr The css attribute
5986          * @param {String} defaultValue The default value to use when a valid color isn't found
5987          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5988          * color anims.
5989          */
5990         getColor : function(attr, defaultValue, prefix){
5991             var v = this.getStyle(attr),
5992                 color = Ext.isDefined(prefix) ? prefix : '#',
5993                 h;
5994
5995             if(!v || /transparent|inherit/.test(v)){
5996                 return defaultValue;
5997             }
5998             if(/^r/.test(v)){
5999                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6000                     h = parseInt(s, 10);
6001                     color += (h < 16 ? '0' : '') + h.toString(16);
6002                 });
6003             }else{
6004                 v = v.replace('#', '');
6005                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6006             }
6007             return(color.length > 5 ? color.toLowerCase() : defaultValue);
6008         },
6009
6010         /**
6011          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6012          * @param {String/Object} property The style property to be set, or an object of multiple styles.
6013          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6014          * @return {Ext.Element} this
6015          */
6016         setStyle : function(prop, value){
6017             var tmp,
6018                 style,
6019                 camel;
6020             if (!Ext.isObject(prop)) {
6021                 tmp = {};
6022                 tmp[prop] = value;
6023                 prop = tmp;
6024             }
6025             for (style in prop) {
6026                 value = prop[style];
6027                 style == 'opacity' ?
6028                     this.setOpacity(value) :
6029                     this.dom.style[chkCache(style)] = value;
6030             }
6031             return this;
6032         },
6033
6034         /**
6035          * Set the opacity of the element
6036          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6037          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6038          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6039          * @return {Ext.Element} this
6040          */
6041          setOpacity : function(opacity, animate){
6042             var me = this,
6043                 s = me.dom.style;
6044
6045             if(!animate || !me.anim){
6046                 if(Ext.isIE){
6047                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6048                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6049
6050                     s.zoom = 1;
6051                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6052                 }else{
6053                     s.opacity = opacity;
6054                 }
6055             }else{
6056                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6057             }
6058             return me;
6059         },
6060
6061         /**
6062          * Clears any opacity settings from this element. Required in some cases for IE.
6063          * @return {Ext.Element} this
6064          */
6065         clearOpacity : function(){
6066             var style = this.dom.style;
6067             if(Ext.isIE){
6068                 if(!Ext.isEmpty(style.filter)){
6069                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6070                 }
6071             }else{
6072                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6073             }
6074             return this;
6075         },
6076
6077         /**
6078          * Returns the offset height of the element
6079          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6080          * @return {Number} The element's height
6081          */
6082         getHeight : function(contentHeight){
6083             var me = this,
6084                 dom = me.dom,
6085                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6086                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6087
6088             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6089             return h < 0 ? 0 : h;
6090         },
6091
6092         /**
6093          * Returns the offset width of the element
6094          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6095          * @return {Number} The element's width
6096          */
6097         getWidth : function(contentWidth){
6098             var me = this,
6099                 dom = me.dom,
6100                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6101                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6102             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6103             return w < 0 ? 0 : w;
6104         },
6105
6106         /**
6107          * Set the width of this Element.
6108          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6109          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6110          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6111          * </ul></div>
6112          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6113          * @return {Ext.Element} this
6114          */
6115         setWidth : function(width, animate){
6116             var me = this;
6117             width = me.adjustWidth(width);
6118             !animate || !me.anim ?
6119                 me.dom.style.width = me.addUnits(width) :
6120                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6121             return me;
6122         },
6123
6124         /**
6125          * Set the height of this Element.
6126          * <pre><code>
6127 // change the height to 200px and animate with default configuration
6128 Ext.fly('elementId').setHeight(200, true);
6129
6130 // change the height to 150px and animate with a custom configuration
6131 Ext.fly('elId').setHeight(150, {
6132     duration : .5, // animation will have a duration of .5 seconds
6133     // will change the content to "finished"
6134     callback: function(){ this.{@link #update}("finished"); }
6135 });
6136          * </code></pre>
6137          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6138          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6139          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6140          * </ul></div>
6141          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6142          * @return {Ext.Element} this
6143          */
6144          setHeight : function(height, animate){
6145             var me = this;
6146             height = me.adjustHeight(height);
6147             !animate || !me.anim ?
6148                 me.dom.style.height = me.addUnits(height) :
6149                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6150             return me;
6151         },
6152
6153         /**
6154          * Gets the width of the border(s) for the specified side(s)
6155          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6156          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6157          * @return {Number} The width of the sides passed added together
6158          */
6159         getBorderWidth : function(side){
6160             return this.addStyles(side, borders);
6161         },
6162
6163         /**
6164          * Gets the width of the padding(s) for the specified side(s)
6165          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6166          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6167          * @return {Number} The padding of the sides passed added together
6168          */
6169         getPadding : function(side){
6170             return this.addStyles(side, paddings);
6171         },
6172
6173         /**
6174          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6175          * @return {Ext.Element} this
6176          */
6177         clip : function(){
6178             var me = this,
6179                 dom = me.dom;
6180
6181             if(!data(dom, ISCLIPPED)){
6182                 data(dom, ISCLIPPED, true);
6183                 data(dom, ORIGINALCLIP, {
6184                     o: me.getStyle(OVERFLOW),
6185                     x: me.getStyle(OVERFLOWX),
6186                     y: me.getStyle(OVERFLOWY)
6187                 });
6188                 me.setStyle(OVERFLOW, HIDDEN);
6189                 me.setStyle(OVERFLOWX, HIDDEN);
6190                 me.setStyle(OVERFLOWY, HIDDEN);
6191             }
6192             return me;
6193         },
6194
6195         /**
6196          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6197          * @return {Ext.Element} this
6198          */
6199         unclip : function(){
6200             var me = this,
6201                 dom = me.dom;
6202
6203             if(data(dom, ISCLIPPED)){
6204                 data(dom, ISCLIPPED, false);
6205                 var o = data(dom, ORIGINALCLIP);
6206                 if(o.o){
6207                     me.setStyle(OVERFLOW, o.o);
6208                 }
6209                 if(o.x){
6210                     me.setStyle(OVERFLOWX, o.x);
6211                 }
6212                 if(o.y){
6213                     me.setStyle(OVERFLOWY, o.y);
6214                 }
6215             }
6216             return me;
6217         },
6218
6219         // private
6220         addStyles : function(sides, styles){
6221             var val = 0,
6222                 m = sides.match(/\w/g),
6223                 s;
6224             for (var i=0, len=m.length; i<len; i++) {
6225                 s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);
6226                 if (s) {
6227                     val += MATH.abs(s);
6228                 }
6229             }
6230             return val;
6231         },
6232
6233         margins : margins
6234     }
6235 }()
6236 );
6237 /**
6238  * @class Ext.Element
6239  */
6240
6241 // special markup used throughout Ext when box wrapping elements
6242 Ext.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
6243
6244 Ext.Element.addMethods(function(){
6245     var INTERNAL = "_internal",
6246         pxMatch = /(\d+\.?\d+)px/;
6247     return {
6248         /**
6249          * More flexible version of {@link #setStyle} for setting style properties.
6250          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6251          * a function which returns such a specification.
6252          * @return {Ext.Element} this
6253          */
6254         applyStyles : function(style){
6255             Ext.DomHelper.applyStyles(this.dom, style);
6256             return this;
6257         },
6258
6259         /**
6260          * Returns an object with properties matching the styles requested.
6261          * For example, el.getStyles('color', 'font-size', 'width') might return
6262          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6263          * @param {String} style1 A style name
6264          * @param {String} style2 A style name
6265          * @param {String} etc.
6266          * @return {Object} The style object
6267          */
6268         getStyles : function(){
6269             var ret = {};
6270             Ext.each(arguments, function(v) {
6271                ret[v] = this.getStyle(v);
6272             },
6273             this);
6274             return ret;
6275         },
6276
6277         // private  ==> used by ext full
6278         setOverflow : function(v){
6279             var dom = this.dom;
6280             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6281                 dom.style.overflow = 'hidden';
6282                 (function(){dom.style.overflow = 'auto';}).defer(1);
6283             }else{
6284                 dom.style.overflow = v;
6285             }
6286         },
6287
6288        /**
6289         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6290         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6291         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6292         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup
6293         * is of this form:</p>
6294         * <pre><code>
6295     Ext.Element.boxMarkup =
6296     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
6297      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
6298      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
6299         * </code></pre>
6300         * <p>Example usage:</p>
6301         * <pre><code>
6302     // Basic box wrap
6303     Ext.get("foo").boxWrap();
6304
6305     // You can also add a custom class and use CSS inheritance rules to customize the box look.
6306     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6307     // for how to create a custom box wrap style.
6308     Ext.get("foo").boxWrap().addClass("x-box-blue");
6309         * </code></pre>
6310         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6311         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6312         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6313         * also supply all of the necessary rules.
6314         * @return {Ext.Element} The outermost wrapping element of the created box structure.
6315         */
6316         boxWrap : function(cls){
6317             cls = cls || 'x-box';
6318             var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + String.format(Ext.Element.boxMarkup, cls) + "</div>"));        //String.format('<div class="{0}">'+Ext.Element.boxMarkup+'</div>', cls)));
6319             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6320             return el;
6321         },
6322
6323         /**
6324          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6325          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6326          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6327          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6328          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6329          * </ul></div>
6330          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6331          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6332          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6333          * </ul></div>
6334          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6335          * @return {Ext.Element} this
6336          */
6337         setSize : function(width, height, animate){
6338             var me = this;
6339             if(Ext.isObject(width)){ // in case of object from getSize()
6340                 height = width.height;
6341                 width = width.width;
6342             }
6343             width = me.adjustWidth(width);
6344             height = me.adjustHeight(height);
6345             if(!animate || !me.anim){
6346                 me.dom.style.width = me.addUnits(width);
6347                 me.dom.style.height = me.addUnits(height);
6348             }else{
6349                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6350             }
6351             return me;
6352         },
6353
6354         /**
6355          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6356          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6357          * if a height has not been set using CSS.
6358          * @return {Number}
6359          */
6360         getComputedHeight : function(){
6361             var me = this,
6362                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6363             if(!h){
6364                 h = parseFloat(me.getStyle('height')) || 0;
6365                 if(!me.isBorderBox()){
6366                     h += me.getFrameWidth('tb');
6367                 }
6368             }
6369             return h;
6370         },
6371
6372         /**
6373          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6374          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6375          * if a width has not been set using CSS.
6376          * @return {Number}
6377          */
6378         getComputedWidth : function(){
6379             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6380             if(!w){
6381                 w = parseFloat(this.getStyle('width')) || 0;
6382                 if(!this.isBorderBox()){
6383                     w += this.getFrameWidth('lr');
6384                 }
6385             }
6386             return w;
6387         },
6388
6389         /**
6390          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6391          for more information about the sides.
6392          * @param {String} sides
6393          * @return {Number}
6394          */
6395         getFrameWidth : function(sides, onlyContentBox){
6396             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6397         },
6398
6399         /**
6400          * Sets up event handlers to add and remove a css class when the mouse is over this element
6401          * @param {String} className
6402          * @return {Ext.Element} this
6403          */
6404         addClassOnOver : function(className){
6405             this.hover(
6406                 function(){
6407                     Ext.fly(this, INTERNAL).addClass(className);
6408                 },
6409                 function(){
6410                     Ext.fly(this, INTERNAL).removeClass(className);
6411                 }
6412             );
6413             return this;
6414         },
6415
6416         /**
6417          * Sets up event handlers to add and remove a css class when this element has the focus
6418          * @param {String} className
6419          * @return {Ext.Element} this
6420          */
6421         addClassOnFocus : function(className){
6422             this.on("focus", function(){
6423                 Ext.fly(this, INTERNAL).addClass(className);
6424             }, this.dom);
6425             this.on("blur", function(){
6426                 Ext.fly(this, INTERNAL).removeClass(className);
6427             }, this.dom);
6428             return this;
6429         },
6430
6431         /**
6432          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
6433          * @param {String} className
6434          * @return {Ext.Element} this
6435          */
6436         addClassOnClick : function(className){
6437             var dom = this.dom;
6438             this.on("mousedown", function(){
6439                 Ext.fly(dom, INTERNAL).addClass(className);
6440                 var d = Ext.getDoc(),
6441                     fn = function(){
6442                         Ext.fly(dom, INTERNAL).removeClass(className);
6443                         d.removeListener("mouseup", fn);
6444                     };
6445                 d.on("mouseup", fn);
6446             });
6447             return this;
6448         },
6449
6450         /**
6451          * <p>Returns the dimensions of the element available to lay content out in.<p>
6452          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6453          * example:<pre><code>
6454         var vpSize = Ext.getBody().getViewSize();
6455
6456         // all Windows created afterwards will have a default value of 90% height and 95% width
6457         Ext.Window.override({
6458             width: vpSize.width * 0.9,
6459             height: vpSize.height * 0.95
6460         });
6461         // To handle window resizing you would have to hook onto onWindowResize.
6462         * </code></pre>
6463         *
6464         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6465         * To obtain the size including scrollbars, use getStyleSize
6466         *
6467         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6468         */
6469
6470         getViewSize : function(){
6471             var doc = document,
6472                 d = this.dom,
6473                 isDoc = (d == doc || d == doc.body);
6474
6475             // If the body, use Ext.lib.Dom
6476             if (isDoc) {
6477                 var extdom = Ext.lib.Dom;
6478                 return {
6479                     width : extdom.getViewWidth(),
6480                     height : extdom.getViewHeight()
6481                 };
6482
6483             // Else use clientHeight/clientWidth
6484             } else {
6485                 return {
6486                     width : d.clientWidth,
6487                     height : d.clientHeight
6488                 }
6489             }
6490         },
6491
6492         /**
6493         * <p>Returns the dimensions of the element available to lay content out in.<p>
6494         *
6495         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6496         * To obtain the size excluding scrollbars, use getViewSize
6497         *
6498         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6499         */
6500
6501         getStyleSize : function(){
6502             var me = this,
6503                 w, h,
6504                 doc = document,
6505                 d = this.dom,
6506                 isDoc = (d == doc || d == doc.body),
6507                 s = d.style;
6508
6509             // If the body, use Ext.lib.Dom
6510             if (isDoc) {
6511                 var extdom = Ext.lib.Dom;
6512                 return {
6513                     width : extdom.getViewWidth(),
6514                     height : extdom.getViewHeight()
6515                 }
6516             }
6517             // Use Styles if they are set
6518             if(s.width && s.width != 'auto'){
6519                 w = parseFloat(s.width);
6520                 if(me.isBorderBox()){
6521                    w -= me.getFrameWidth('lr');
6522                 }
6523             }
6524             // Use Styles if they are set
6525             if(s.height && s.height != 'auto'){
6526                 h = parseFloat(s.height);
6527                 if(me.isBorderBox()){
6528                    h -= me.getFrameWidth('tb');
6529                 }
6530             }
6531             // Use getWidth/getHeight if style not set.
6532             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6533         },
6534
6535         /**
6536          * Returns the size of the element.
6537          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6538          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6539          */
6540         getSize : function(contentSize){
6541             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6542         },
6543
6544         /**
6545          * Forces the browser to repaint this element
6546          * @return {Ext.Element} this
6547          */
6548         repaint : function(){
6549             var dom = this.dom;
6550             this.addClass("x-repaint");
6551             setTimeout(function(){
6552                 Ext.fly(dom).removeClass("x-repaint");
6553             }, 1);
6554             return this;
6555         },
6556
6557         /**
6558          * Disables text selection for this element (normalized across browsers)
6559          * @return {Ext.Element} this
6560          */
6561         unselectable : function(){
6562             this.dom.unselectable = "on";
6563             return this.swallowEvent("selectstart", true).
6564                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6565                         addClass("x-unselectable");
6566         },
6567
6568         /**
6569          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6570          * then it returns the calculated width of the sides (see getPadding)
6571          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6572          * @return {Object/Number}
6573          */
6574         getMargins : function(side){
6575             var me = this,
6576                 key,
6577                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6578                 o = {};
6579
6580             if (!side) {
6581                 for (key in me.margins){
6582                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6583                 }
6584                 return o;
6585             } else {
6586                 return me.addStyles.call(me, side, me.margins);
6587             }
6588         }
6589     };
6590 }());
6591 /**
6592  * @class Ext.Element
6593  */
6594 (function(){
6595 var D = Ext.lib.Dom,
6596         LEFT = "left",
6597         RIGHT = "right",
6598         TOP = "top",
6599         BOTTOM = "bottom",
6600         POSITION = "position",
6601         STATIC = "static",
6602         RELATIVE = "relative",
6603         AUTO = "auto",
6604         ZINDEX = "z-index";
6605
6606 Ext.Element.addMethods({
6607         /**
6608       * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6609       * @return {Number} The X position of the element
6610       */
6611     getX : function(){
6612         return D.getX(this.dom);
6613     },
6614
6615     /**
6616       * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6617       * @return {Number} The Y position of the element
6618       */
6619     getY : function(){
6620         return D.getY(this.dom);
6621     },
6622
6623     /**
6624       * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6625       * @return {Array} The XY position of the element
6626       */
6627     getXY : function(){
6628         return D.getXY(this.dom);
6629     },
6630
6631     /**
6632       * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.
6633       * @param {Mixed} element The element to get the offsets from.
6634       * @return {Array} The XY page offsets (e.g. [100, -200])
6635       */
6636     getOffsetsTo : function(el){
6637         var o = this.getXY(),
6638                 e = Ext.fly(el, '_internal').getXY();
6639         return [o[0]-e[0],o[1]-e[1]];
6640     },
6641
6642     /**
6643      * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6644      * @param {Number} The X position of the element
6645      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6646      * @return {Ext.Element} this
6647      */
6648     setX : function(x, animate){            
6649             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6650     },
6651
6652     /**
6653      * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6654      * @param {Number} The Y position of the element
6655      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6656      * @return {Ext.Element} this
6657      */
6658     setY : function(y, animate){            
6659             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6660     },
6661
6662     /**
6663      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6664      * @param {String} left The left CSS property value
6665      * @return {Ext.Element} this
6666      */
6667     setLeft : function(left){
6668         this.setStyle(LEFT, this.addUnits(left));
6669         return this;
6670     },
6671
6672     /**
6673      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6674      * @param {String} top The top CSS property value
6675      * @return {Ext.Element} this
6676      */
6677     setTop : function(top){
6678         this.setStyle(TOP, this.addUnits(top));
6679         return this;
6680     },
6681
6682     /**
6683      * Sets the element's CSS right style.
6684      * @param {String} right The right CSS property value
6685      * @return {Ext.Element} this
6686      */
6687     setRight : function(right){
6688         this.setStyle(RIGHT, this.addUnits(right));
6689         return this;
6690     },
6691
6692     /**
6693      * Sets the element's CSS bottom style.
6694      * @param {String} bottom The bottom CSS property value
6695      * @return {Ext.Element} this
6696      */
6697     setBottom : function(bottom){
6698         this.setStyle(BOTTOM, this.addUnits(bottom));
6699         return this;
6700     },
6701
6702     /**
6703      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6704      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6705      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6706      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6707      * @return {Ext.Element} this
6708      */
6709     setXY : function(pos, animate){
6710             var me = this;
6711         if(!animate || !me.anim){
6712             D.setXY(me.dom, pos);
6713         }else{
6714             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6715         }
6716         return me;
6717     },
6718
6719     /**
6720      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6721      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6722      * @param {Number} x X value for new position (coordinates are page-based)
6723      * @param {Number} y Y value for new position (coordinates are page-based)
6724      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6725      * @return {Ext.Element} this
6726      */
6727     setLocation : function(x, y, animate){
6728         return this.setXY([x, y], this.animTest(arguments, animate, 2));
6729     },
6730
6731     /**
6732      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6733      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6734      * @param {Number} x X value for new position (coordinates are page-based)
6735      * @param {Number} y Y value for new position (coordinates are page-based)
6736      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6737      * @return {Ext.Element} this
6738      */
6739     moveTo : function(x, y, animate){
6740         return this.setXY([x, y], this.animTest(arguments, animate, 2));        
6741     },    
6742     
6743     /**
6744      * Gets the left X coordinate
6745      * @param {Boolean} local True to get the local css position instead of page coordinate
6746      * @return {Number}
6747      */
6748     getLeft : function(local){
6749             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6750     },
6751
6752     /**
6753      * Gets the right X coordinate of the element (element X position + element width)
6754      * @param {Boolean} local True to get the local css position instead of page coordinate
6755      * @return {Number}
6756      */
6757     getRight : function(local){
6758             var me = this;
6759             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6760     },
6761
6762     /**
6763      * Gets the top Y coordinate
6764      * @param {Boolean} local True to get the local css position instead of page coordinate
6765      * @return {Number}
6766      */
6767     getTop : function(local) {
6768             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6769     },
6770
6771     /**
6772      * Gets the bottom Y coordinate of the element (element Y position + element height)
6773      * @param {Boolean} local True to get the local css position instead of page coordinate
6774      * @return {Number}
6775      */
6776     getBottom : function(local){
6777             var me = this;
6778             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6779     },
6780
6781     /**
6782     * Initializes positioning on this element. If a desired position is not passed, it will make the
6783     * the element positioned relative IF it is not already positioned.
6784     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6785     * @param {Number} zIndex (optional) The zIndex to apply
6786     * @param {Number} x (optional) Set the page X position
6787     * @param {Number} y (optional) Set the page Y position
6788     */
6789     position : function(pos, zIndex, x, y){
6790             var me = this;
6791             
6792         if(!pos && me.isStyle(POSITION, STATIC)){           
6793             me.setStyle(POSITION, RELATIVE);           
6794         } else if(pos) {
6795             me.setStyle(POSITION, pos);
6796         }
6797         if(zIndex){
6798             me.setStyle(ZINDEX, zIndex);
6799         }
6800         if(x || y) me.setXY([x || false, y || false]);
6801     },
6802
6803     /**
6804     * Clear positioning back to the default when the document was loaded
6805     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6806     * @return {Ext.Element} this
6807      */
6808     clearPositioning : function(value){
6809         value = value || '';
6810         this.setStyle({
6811             left : value,
6812             right : value,
6813             top : value,
6814             bottom : value,
6815             "z-index" : "",
6816             position : STATIC
6817         });
6818         return this;
6819     },
6820
6821     /**
6822     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6823     * snapshot before performing an update and then restoring the element.
6824     * @return {Object}
6825     */
6826     getPositioning : function(){
6827         var l = this.getStyle(LEFT);
6828         var t = this.getStyle(TOP);
6829         return {
6830             "position" : this.getStyle(POSITION),
6831             "left" : l,
6832             "right" : l ? "" : this.getStyle(RIGHT),
6833             "top" : t,
6834             "bottom" : t ? "" : this.getStyle(BOTTOM),
6835             "z-index" : this.getStyle(ZINDEX)
6836         };
6837     },
6838     
6839     /**
6840     * Set positioning with an object returned by getPositioning().
6841     * @param {Object} posCfg
6842     * @return {Ext.Element} this
6843      */
6844     setPositioning : function(pc){
6845             var me = this,
6846                 style = me.dom.style;
6847                 
6848         me.setStyle(pc);
6849         
6850         if(pc.right == AUTO){
6851             style.right = "";
6852         }
6853         if(pc.bottom == AUTO){
6854             style.bottom = "";
6855         }
6856         
6857         return me;
6858     },    
6859         
6860     /**
6861      * Translates the passed page coordinates into left/top css values for this element
6862      * @param {Number/Array} x The page x or an array containing [x, y]
6863      * @param {Number} y (optional) The page y, required if x is not an array
6864      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6865      */
6866     translatePoints : function(x, y){                
6867             y = isNaN(x[1]) ? y : x[1];
6868         x = isNaN(x[0]) ? x : x[0];
6869         var me = this,
6870                 relative = me.isStyle(POSITION, RELATIVE),
6871                 o = me.getXY(),
6872                 l = parseInt(me.getStyle(LEFT), 10),
6873                 t = parseInt(me.getStyle(TOP), 10);
6874         
6875         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6876         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        
6877
6878         return {left: (x - o[0] + l), top: (y - o[1] + t)}; 
6879     },
6880     
6881     animTest : function(args, animate, i) {
6882         return !!animate && this.preanim ? this.preanim(args, i) : false;
6883     }
6884 });
6885 })();/**
6886  * @class Ext.Element
6887  */
6888 Ext.Element.addMethods({
6889     /**
6890      * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
6891      * @param {Object} box The box to fill {x, y, width, height}
6892      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6893      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6894      * @return {Ext.Element} this
6895      */
6896     setBox : function(box, adjust, animate){
6897         var me = this,
6898                 w = box.width, 
6899                 h = box.height;
6900         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6901            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6902            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6903         }
6904         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6905         return me;
6906     },
6907
6908     /**
6909      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6910      * set another Element's size/location to match this element.
6911      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6912      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6913      * @return {Object} box An object in the format<pre><code>
6914 {
6915     x: &lt;Element's X position>,
6916     y: &lt;Element's Y position>,
6917     width: &lt;Element's width>,
6918     height: &lt;Element's height>,
6919     bottom: &lt;Element's lower bound>,
6920     right: &lt;Element's rightmost bound>
6921 }
6922 </code></pre>
6923      * The returned object may also be addressed as an Array where index 0 contains the X position
6924      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6925      */
6926         getBox : function(contentBox, local) {      
6927             var me = this,
6928                 xy,
6929                 left,
6930                 top,
6931                 getBorderWidth = me.getBorderWidth,
6932                 getPadding = me.getPadding, 
6933                 l,
6934                 r,
6935                 t,
6936                 b;
6937         if(!local){
6938             xy = me.getXY();
6939         }else{
6940             left = parseInt(me.getStyle("left"), 10) || 0;
6941             top = parseInt(me.getStyle("top"), 10) || 0;
6942             xy = [left, top];
6943         }
6944         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
6945         if(!contentBox){
6946             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
6947         }else{
6948             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
6949             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
6950             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
6951             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
6952             bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
6953         }
6954         bx.right = bx.x + bx.width;
6955         bx.bottom = bx.y + bx.height;
6956         return bx;
6957         },
6958         
6959     /**
6960      * Move this element relative to its current position.
6961      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
6962      * @param {Number} distance How far to move the element in pixels
6963      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6964      * @return {Ext.Element} this
6965      */
6966      move : function(direction, distance, animate){
6967         var me = this,          
6968                 xy = me.getXY(),
6969                 x = xy[0],
6970                 y = xy[1],              
6971                 left = [x - distance, y],
6972                 right = [x + distance, y],
6973                 top = [x, y - distance],
6974                 bottom = [x, y + distance],
6975                 hash = {
6976                         l :     left,
6977                         left : left,
6978                         r : right,
6979                         right : right,
6980                         t : top,
6981                         top : top,
6982                         up : top,
6983                         b : bottom, 
6984                         bottom : bottom,
6985                         down : bottom                           
6986                 };
6987         
6988             direction = direction.toLowerCase();    
6989             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
6990     },
6991     
6992     /**
6993      * Quick set left and top adding default units
6994      * @param {String} left The left CSS property value
6995      * @param {String} top The top CSS property value
6996      * @return {Ext.Element} this
6997      */
6998      setLeftTop : function(left, top){
6999             var me = this,
7000                 style = me.dom.style;
7001         style.left = me.addUnits(left);
7002         style.top = me.addUnits(top);
7003         return me;
7004     },
7005     
7006     /**
7007      * Returns the region of the given element.
7008      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7009      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7010      */
7011     getRegion : function(){
7012         return Ext.lib.Dom.getRegion(this.dom);
7013     },
7014     
7015     /**
7016      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7017      * @param {Number} x X value for new position (coordinates are page-based)
7018      * @param {Number} y Y value for new position (coordinates are page-based)
7019      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7020      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7021      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7022      * </ul></div>
7023      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7024      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7025      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7026      * </ul></div>
7027      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7028      * @return {Ext.Element} this
7029      */
7030     setBounds : function(x, y, width, height, animate){
7031             var me = this;
7032         if (!animate || !me.anim) {
7033             me.setSize(width, height);
7034             me.setLocation(x, y);
7035         } else {
7036             me.anim({points: {to: [x, y]}, 
7037                          width: {to: me.adjustWidth(width)}, 
7038                          height: {to: me.adjustHeight(height)}},
7039                      me.preanim(arguments, 4), 
7040                      'motion');
7041         }
7042         return me;
7043     },
7044
7045     /**
7046      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7047      * @param {Ext.lib.Region} region The region to fill
7048      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7049      * @return {Ext.Element} this
7050      */
7051     setRegion : function(region, animate) {
7052         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7053     }
7054 });/**
7055  * @class Ext.Element
7056  */
7057 Ext.Element.addMethods({
7058     /**
7059      * Returns true if this element is scrollable.
7060      * @return {Boolean}
7061      */
7062     isScrollable : function(){
7063         var dom = this.dom;
7064         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7065     },
7066
7067     /**
7068      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
7069      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7070      * @param {Number} value The new scroll value.
7071      * @return {Element} this
7072      */
7073     scrollTo : function(side, value){
7074         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7075         return this;
7076     },
7077
7078     /**
7079      * Returns the current scroll position of the element.
7080      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7081      */
7082     getScroll : function(){
7083         var d = this.dom, 
7084             doc = document,
7085             body = doc.body,
7086             docElement = doc.documentElement,
7087             l,
7088             t,
7089             ret;
7090
7091         if(d == doc || d == body){
7092             if(Ext.isIE && Ext.isStrict){
7093                 l = docElement.scrollLeft; 
7094                 t = docElement.scrollTop;
7095             }else{
7096                 l = window.pageXOffset;
7097                 t = window.pageYOffset;
7098             }
7099             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7100         }else{
7101             ret = {left: d.scrollLeft, top: d.scrollTop};
7102         }
7103         return ret;
7104     }
7105 });/**
7106  * @class Ext.Element
7107  */
7108 Ext.Element.addMethods({
7109     /**
7110      * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
7111      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7112      * @param {Number} value The new scroll value
7113      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7114      * @return {Element} this
7115      */
7116     scrollTo : function(side, value, animate){
7117         var top = /top/i.test(side), //check if we're scrolling top or left
7118                 me = this,
7119                 dom = me.dom,
7120             prop;
7121         if (!animate || !me.anim) {
7122             prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction
7123             dom[prop] = value;
7124         }else{
7125             prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7126             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},
7127                          me.preanim(arguments, 2), 'scroll');
7128         }
7129         return me;
7130     },
7131     
7132     /**
7133      * Scrolls this element into view within the passed container.
7134      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
7135      * string (id), dom node, or Ext.Element.
7136      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7137      * @return {Ext.Element} this
7138      */
7139     scrollIntoView : function(container, hscroll){
7140         var c = Ext.getDom(container) || Ext.getBody().dom,
7141                 el = this.dom,
7142                 o = this.getOffsetsTo(c),
7143             l = o[0] + c.scrollLeft,
7144             t = o[1] + c.scrollTop,
7145             b = t + el.offsetHeight,
7146             r = l + el.offsetWidth,
7147                 ch = c.clientHeight,
7148                 ct = parseInt(c.scrollTop, 10),
7149                 cl = parseInt(c.scrollLeft, 10),
7150                 cb = ct + ch,
7151                 cr = cl + c.clientWidth;
7152
7153         if (el.offsetHeight > ch || t < ct) {
7154                 c.scrollTop = t;
7155         } else if (b > cb){
7156             c.scrollTop = b-ch;
7157         }
7158         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
7159
7160         if(hscroll !== false){
7161                         if(el.offsetWidth > c.clientWidth || l < cl){
7162                 c.scrollLeft = l;
7163             }else if(r > cr){
7164                 c.scrollLeft = r - c.clientWidth;
7165             }
7166             c.scrollLeft = c.scrollLeft;
7167         }
7168         return this;
7169     },
7170
7171     // private
7172     scrollChildIntoView : function(child, hscroll){
7173         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7174     },
7175     
7176     /**
7177      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7178      * within this element's scrollable range.
7179      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7180      * @param {Number} distance How far to scroll the element in pixels
7181      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7182      * @return {Boolean} Returns true if a scroll was triggered or false if the element
7183      * was scrolled as far as it could go.
7184      */
7185      scroll : function(direction, distance, animate){
7186          if(!this.isScrollable()){
7187              return;
7188          }
7189          var el = this.dom,
7190             l = el.scrollLeft, t = el.scrollTop,
7191             w = el.scrollWidth, h = el.scrollHeight,
7192             cw = el.clientWidth, ch = el.clientHeight,
7193             scrolled = false, v,
7194             hash = {
7195                 l: Math.min(l + distance, w-cw),
7196                 r: v = Math.max(l - distance, 0),
7197                 t: Math.max(t - distance, 0),
7198                 b: Math.min(t + distance, h-ch)
7199             };
7200             hash.d = hash.b;
7201             hash.u = hash.t;
7202             
7203          direction = direction.substr(0, 1);
7204          if((v = hash[direction]) > -1){
7205             scrolled = true;
7206             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7207          }
7208          return scrolled;
7209     }
7210 });/**
7211  * @class Ext.Element
7212  */
7213 /**
7214  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7215  * @static
7216  * @type Number
7217  */
7218 Ext.Element.VISIBILITY = 1;
7219 /**
7220  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7221  * @static
7222  * @type Number
7223  */
7224 Ext.Element.DISPLAY = 2;
7225
7226 Ext.Element.addMethods(function(){
7227     var VISIBILITY = "visibility",
7228         DISPLAY = "display",
7229         HIDDEN = "hidden",
7230         OFFSETS = "offsets",
7231         NONE = "none",
7232         ORIGINALDISPLAY = 'originalDisplay',
7233         VISMODE = 'visibilityMode',
7234         ELDISPLAY = Ext.Element.DISPLAY,
7235         data = Ext.Element.data,
7236         getDisplay = function(dom){
7237             var d = data(dom, ORIGINALDISPLAY);
7238             if(d === undefined){
7239                 data(dom, ORIGINALDISPLAY, d = '');
7240             }
7241             return d;
7242         },
7243         getVisMode = function(dom){
7244             var m = data(dom, VISMODE);
7245             if(m === undefined){
7246                 data(dom, VISMODE, m = 1);
7247             }
7248             return m;
7249         };
7250
7251     return {
7252         /**
7253          * The element's default display mode  (defaults to "")
7254          * @type String
7255          */
7256         originalDisplay : "",
7257         visibilityMode : 1,
7258
7259         /**
7260          * Sets the element's visibility mode. When setVisible() is called it
7261          * will use this to determine whether to set the visibility or the display property.
7262          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7263          * @return {Ext.Element} this
7264          */
7265         setVisibilityMode : function(visMode){
7266             data(this.dom, VISMODE, visMode);
7267             return this;
7268         },
7269
7270         /**
7271          * Perform custom animation on this element.
7272          * <div><ul class="mdetail-params">
7273          * <li><u>Animation Properties</u></li>
7274          *
7275          * <p>The Animation Control Object enables gradual transitions for any member of an
7276          * element's style object that takes a numeric value including but not limited to
7277          * these properties:</p><div><ul class="mdetail-params">
7278          * <li><tt>bottom, top, left, right</tt></li>
7279          * <li><tt>height, width</tt></li>
7280          * <li><tt>margin, padding</tt></li>
7281          * <li><tt>borderWidth</tt></li>
7282          * <li><tt>opacity</tt></li>
7283          * <li><tt>fontSize</tt></li>
7284          * <li><tt>lineHeight</tt></li>
7285          * </ul></div>
7286          *
7287          *
7288          * <li><u>Animation Property Attributes</u></li>
7289          *
7290          * <p>Each Animation Property is a config object with optional properties:</p>
7291          * <div><ul class="mdetail-params">
7292          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>
7293          * <li><tt>from</tt> : ignore current value, start from this value</li>
7294          * <li><tt>to</tt>*  : start at current value, go to this value</li>
7295          * <li><tt>unit</tt> : any allowable unit specification</li>
7296          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7297          * </ul></div>
7298          *
7299          * <li><u>Animation Types</u></li>
7300          *
7301          * <p>The supported animation types:</p><div><ul class="mdetail-params">
7302          * <li><tt>'run'</tt> : Default
7303          * <pre><code>
7304 var el = Ext.get('complexEl');
7305 el.animate(
7306     // animation control object
7307     {
7308         borderWidth: {to: 3, from: 0},
7309         opacity: {to: .3, from: 1},
7310         height: {to: 50, from: el.getHeight()},
7311         width: {to: 300, from: el.getWidth()},
7312         top  : {by: - 100, unit: 'px'},
7313     },
7314     0.35,      // animation duration
7315     null,      // callback
7316     'easeOut', // easing method
7317     'run'      // animation type ('run','color','motion','scroll')
7318 );
7319          * </code></pre>
7320          * </li>
7321          * <li><tt>'color'</tt>
7322          * <p>Animates transition of background, text, or border colors.</p>
7323          * <pre><code>
7324 el.animate(
7325     // animation control object
7326     {
7327         color: { to: '#06e' },
7328         backgroundColor: { to: '#e06' }
7329     },
7330     0.35,      // animation duration
7331     null,      // callback
7332     'easeOut', // easing method
7333     'color'    // animation type ('run','color','motion','scroll')
7334 );
7335          * </code></pre>
7336          * </li>
7337          *
7338          * <li><tt>'motion'</tt>
7339          * <p>Animates the motion of an element to/from specific points using optional bezier
7340          * way points during transit.</p>
7341          * <pre><code>
7342 el.animate(
7343     // animation control object
7344     {
7345         borderWidth: {to: 3, from: 0},
7346         opacity: {to: .3, from: 1},
7347         height: {to: 50, from: el.getHeight()},
7348         width: {to: 300, from: el.getWidth()},
7349         top  : {by: - 100, unit: 'px'},
7350         points: {
7351             to: [50, 100],  // go to this point
7352             control: [      // optional bezier way points
7353                 [ 600, 800],
7354                 [-100, 200]
7355             ]
7356         }
7357     },
7358     3000,      // animation duration (milliseconds!)
7359     null,      // callback
7360     'easeOut', // easing method
7361     'motion'   // animation type ('run','color','motion','scroll')
7362 );
7363          * </code></pre>
7364          * </li>
7365          * <li><tt>'scroll'</tt>
7366          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7367          * <pre><code>
7368 el.animate(
7369     // animation control object
7370     {
7371         scroll: {to: [400, 300]}
7372     },
7373     0.35,      // animation duration
7374     null,      // callback
7375     'easeOut', // easing method
7376     'scroll'   // animation type ('run','color','motion','scroll')
7377 );
7378          * </code></pre>
7379          * </li>
7380          * </ul></div>
7381          *
7382          * </ul></div>
7383          *
7384          * @param {Object} args The animation control args
7385          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7386          * @param {Function} onComplete (optional) Function to call when animation completes
7387          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7388          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7389          * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7390          * @return {Ext.Element} this
7391          */
7392         animate : function(args, duration, onComplete, easing, animType){
7393             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7394             return this;
7395         },
7396
7397         /*
7398          * @private Internal animation call
7399          */
7400         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7401             animType = animType || 'run';
7402             opt = opt || {};
7403             var me = this,
7404                 anim = Ext.lib.Anim[animType](
7405                     me.dom,
7406                     args,
7407                     (opt.duration || defaultDur) || .35,
7408                     (opt.easing || defaultEase) || 'easeOut',
7409                     function(){
7410                         if(cb) cb.call(me);
7411                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7412                     },
7413                     me
7414                 );
7415             opt.anim = anim;
7416             return anim;
7417         },
7418
7419         // private legacy anim prep
7420         preanim : function(a, i){
7421             return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7422         },
7423
7424         /**
7425          * Checks whether the element is currently visible using both visibility and display properties.
7426          * @return {Boolean} True if the element is currently visible, else false
7427          */
7428         isVisible : function() {
7429             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7430         },
7431
7432         /**
7433          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7434          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7435          * @param {Boolean} visible Whether the element is visible
7436          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7437          * @return {Ext.Element} this
7438          */
7439          setVisible : function(visible, animate){
7440             var me = this, isDisplay, isVisible, isOffsets,
7441                 dom = me.dom;
7442
7443             // hideMode string override
7444             if (Ext.isString(animate)){
7445                 isDisplay = animate == DISPLAY;
7446                 isVisible = animate == VISIBILITY;
7447                 isOffsets = animate == OFFSETS;
7448                 animate = false;
7449             } else {
7450                 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7451                 isVisible = !isDisplay;
7452             }
7453
7454             if (!animate || !me.anim) {
7455                 if (isDisplay){
7456                     me.setDisplayed(visible);
7457                 } else if (isOffsets){
7458                     if (!visible){
7459                         me.hideModeStyles = {
7460                             position: me.getStyle('position'),
7461                             top: me.getStyle('top'),
7462                             left: me.getStyle('left')
7463                         };
7464
7465                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7466                     } else {
7467                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7468                     }
7469                 }else{
7470                     me.fixDisplay();
7471                     dom.style.visibility = visible ? "visible" : HIDDEN;
7472                 }
7473             }else{
7474                 // closure for composites
7475                 if (visible){
7476                     me.setOpacity(.01);
7477                     me.setVisible(true);
7478                 }
7479                 me.anim({opacity: { to: (visible?1:0) }},
7480                         me.preanim(arguments, 1),
7481                         null,
7482                         .35,
7483                         'easeIn',
7484                         function(){
7485                              if(!visible){
7486                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7487                                  Ext.fly(dom).setOpacity(1);
7488                              }
7489                         });
7490             }
7491             return me;
7492         },
7493
7494         /**
7495          * Toggles the element's visibility or display, depending on visibility mode.
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Ext.Element} this
7498          */
7499         toggle : function(animate){
7500             var me = this;
7501             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7502             return me;
7503         },
7504
7505         /**
7506          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7507          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7508          * @return {Ext.Element} this
7509          */
7510         setDisplayed : function(value) {
7511             if(typeof value == "boolean"){
7512                value = value ? getDisplay(this.dom) : NONE;
7513             }
7514             this.setStyle(DISPLAY, value);
7515             return this;
7516         },
7517
7518         // private
7519         fixDisplay : function(){
7520             var me = this;
7521             if(me.isStyle(DISPLAY, NONE)){
7522                 me.setStyle(VISIBILITY, HIDDEN);
7523                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7524                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7525                     me.setStyle(DISPLAY, "block");
7526                 }
7527             }
7528         },
7529
7530         /**
7531          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7532          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7533          * @return {Ext.Element} this
7534          */
7535         hide : function(animate){
7536             // hideMode override
7537             if (Ext.isString(animate)){
7538                 this.setVisible(false, animate);
7539                 return this;
7540             }
7541             this.setVisible(false, this.preanim(arguments, 0));
7542             return this;
7543         },
7544
7545         /**
7546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7548          * @return {Ext.Element} this
7549          */
7550         show : function(animate){
7551             // hideMode override
7552             if (Ext.isString(animate)){
7553                 this.setVisible(true, animate);
7554                 return this;
7555             }
7556             this.setVisible(true, this.preanim(arguments, 0));
7557             return this;
7558         }
7559     };
7560 }());/**
7561  * @class Ext.Element
7562  */
7563 Ext.Element.addMethods(
7564 function(){
7565     var VISIBILITY = "visibility",
7566         DISPLAY = "display",
7567         HIDDEN = "hidden",
7568         NONE = "none",
7569             XMASKED = "x-masked",
7570                 XMASKEDRELATIVE = "x-masked-relative",
7571         data = Ext.Element.data;
7572
7573         return {
7574                 /**
7575              * Checks whether the element is currently visible using both visibility and display properties.
7576              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7577              * @return {Boolean} True if the element is currently visible, else false
7578              */
7579             isVisible : function(deep) {
7580                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7581                         p = this.dom.parentNode;
7582                 if(deep !== true || !vis){
7583                     return vis;
7584                 }
7585                 while(p && !/^body/i.test(p.tagName)){
7586                     if(!Ext.fly(p, '_isVisible').isVisible()){
7587                         return false;
7588                     }
7589                     p = p.parentNode;
7590                 }
7591                 return true;
7592             },
7593
7594             /**
7595              * Returns true if display is not "none"
7596              * @return {Boolean}
7597              */
7598             isDisplayed : function() {
7599                 return !this.isStyle(DISPLAY, NONE);
7600             },
7601
7602                 /**
7603              * Convenience method for setVisibilityMode(Element.DISPLAY)
7604              * @param {String} display (optional) What to set display to when visible
7605              * @return {Ext.Element} this
7606              */
7607             enableDisplayMode : function(display){
7608                 this.setVisibilityMode(Ext.Element.DISPLAY);
7609                 if(!Ext.isEmpty(display)){
7610                 data(this.dom, 'originalDisplay', display);
7611             }
7612                 return this;
7613             },
7614
7615                 /**
7616              * Puts a mask over this element to disable user interaction. Requires core.css.
7617              * This method can only be applied to elements which accept child nodes.
7618              * @param {String} msg (optional) A message to display in the mask
7619              * @param {String} msgCls (optional) A css class to apply to the msg element
7620              * @return {Element} The mask element
7621              */
7622             mask : function(msg, msgCls){
7623                     var me = this,
7624                         dom = me.dom,
7625                         dh = Ext.DomHelper,
7626                         EXTELMASKMSG = "ext-el-mask-msg",
7627                 el,
7628                 mask;
7629
7630                 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7631                     me.addClass(XMASKEDRELATIVE);
7632                 }
7633                 if((el = data(dom, 'maskMsg'))){
7634                     el.remove();
7635                 }
7636                 if((el = data(dom, 'mask'))){
7637                     el.remove();
7638                 }
7639
7640             mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7641                 data(dom, 'mask', mask);
7642
7643                 me.addClass(XMASKED);
7644                 mask.setDisplayed(true);
7645                 if(typeof msg == 'string'){
7646                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7647                 data(dom, 'maskMsg', mm);
7648                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7649                     mm.dom.firstChild.innerHTML = msg;
7650                     mm.setDisplayed(true);
7651                     mm.center(me);
7652                 }
7653                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7654                     mask.setSize(undefined, me.getHeight());
7655                 }
7656                 return mask;
7657             },
7658
7659             /**
7660              * Removes a previously applied mask.
7661              */
7662             unmask : function(){
7663                     var me = this,
7664                 dom = me.dom,
7665                         mask = data(dom, 'mask'),
7666                         maskMsg = data(dom, 'maskMsg');
7667                 if(mask){
7668                     if(maskMsg){
7669                         maskMsg.remove();
7670                     data(dom, 'maskMsg', undefined);
7671                     }
7672                     mask.remove();
7673                 data(dom, 'mask', undefined);
7674                 }
7675                 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7676             },
7677
7678             /**
7679              * Returns true if this element is masked
7680              * @return {Boolean}
7681              */
7682             isMasked : function(){
7683             var m = data(this.dom, 'mask');
7684                 return m && m.isVisible();
7685             },
7686
7687             /**
7688              * Creates an iframe shim for this element to keep selects and other windowed objects from
7689              * showing through.
7690              * @return {Ext.Element} The new shim element
7691              */
7692             createShim : function(){
7693                 var el = document.createElement('iframe'),
7694                         shim;
7695                 el.frameBorder = '0';
7696                 el.className = 'ext-shim';
7697                 el.src = Ext.SSL_SECURE_URL;
7698                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7699                 shim.autoBoxAdjust = false;
7700                 return shim;
7701             }
7702     };
7703 }());/**
7704  * @class Ext.Element
7705  */
7706 Ext.Element.addMethods({
7707     /**
7708      * Convenience method for constructing a KeyMap
7709      * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
7710      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7711      * @param {Function} fn The function to call
7712      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7713      * @return {Ext.KeyMap} The KeyMap created
7714      */
7715     addKeyListener : function(key, fn, scope){
7716         var config;
7717         if(!Ext.isObject(key) || Ext.isArray(key)){
7718             config = {
7719                 key: key,
7720                 fn: fn,
7721                 scope: scope
7722             };
7723         }else{
7724             config = {
7725                 key : key.key,
7726                 shift : key.shift,
7727                 ctrl : key.ctrl,
7728                 alt : key.alt,
7729                 fn: fn,
7730                 scope: scope
7731             };
7732         }
7733         return new Ext.KeyMap(this, config);
7734     },
7735
7736     /**
7737      * Creates a KeyMap for this element
7738      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7739      * @return {Ext.KeyMap} The KeyMap created
7740      */
7741     addKeyMap : function(config){
7742         return new Ext.KeyMap(this, config);
7743     }
7744 });(function(){
7745     // contants
7746     var NULL = null,
7747         UNDEFINED = undefined,
7748         TRUE = true,
7749         FALSE = false,
7750         SETX = "setX",
7751         SETY = "setY",
7752         SETXY = "setXY",
7753         LEFT = "left",
7754         BOTTOM = "bottom",
7755         TOP = "top",
7756         RIGHT = "right",
7757         HEIGHT = "height",
7758         WIDTH = "width",
7759         POINTS = "points",
7760         HIDDEN = "hidden",
7761         ABSOLUTE = "absolute",
7762         VISIBLE = "visible",
7763         MOTION = "motion",
7764         POSITION = "position",
7765         EASEOUT = "easeOut",
7766         /*
7767          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7768          */
7769         flyEl = new Ext.Element.Flyweight(),
7770         queues = {},
7771         getObject = function(o){
7772             return o || {};
7773         },
7774         fly = function(dom){
7775             flyEl.dom = dom;
7776             flyEl.id = Ext.id(dom);
7777             return flyEl;
7778         },
7779         /*
7780          * Queueing now stored outside of the element due to closure issues
7781          */
7782         getQueue = function(id){
7783             if(!queues[id]){
7784                 queues[id] = [];
7785             }
7786             return queues[id];
7787         },
7788         setQueue = function(id, value){
7789             queues[id] = value;
7790         };
7791         
7792 //Notifies Element that fx methods are available
7793 Ext.enableFx = TRUE;
7794
7795 /**
7796  * @class Ext.Fx
7797  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
7798  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7799  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7800  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7801  * 
7802  * <p><b><u>Method Chaining</u></b></p>
7803  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7804  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7805  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7806  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
7807  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7808  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>
7809  *
7810  * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7811  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7812  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
7813 <pre>
7814 Value  Description
7815 -----  -----------------------------
7816 tl     The top left corner
7817 t      The center of the top edge
7818 tr     The top right corner
7819 l      The center of the left edge
7820 r      The center of the right edge
7821 bl     The bottom left corner
7822 b      The center of the bottom edge
7823 br     The bottom right corner
7824 </pre>
7825  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options
7826  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7827  * 
7828  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the
7829  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7830  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7831  * el.slideIn().highlight();
7832  * </code></pre>
7833  * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7834  * being operated upon is passed as the first parameter.
7835  * 
7836  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7837  * 
7838  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7839  * <li><b><tt>backBoth</tt></b></li>
7840  * <li><b><tt>backIn</tt></b></li>
7841  * <li><b><tt>backOut</tt></b></li>
7842  * <li><b><tt>bounceBoth</tt></b></li>
7843  * <li><b><tt>bounceIn</tt></b></li>
7844  * <li><b><tt>bounceOut</tt></b></li>
7845  * <li><b><tt>easeBoth</tt></b></li>
7846  * <li><b><tt>easeBothStrong</tt></b></li>
7847  * <li><b><tt>easeIn</tt></b></li>
7848  * <li><b><tt>easeInStrong</tt></b></li>
7849  * <li><b><tt>easeNone</tt></b></li>
7850  * <li><b><tt>easeOut</tt></b></li>
7851  * <li><b><tt>easeOutStrong</tt></b></li>
7852  * <li><b><tt>elasticBoth</tt></b></li>
7853  * <li><b><tt>elasticIn</tt></b></li>
7854  * <li><b><tt>elasticOut</tt></b></li>
7855  * </ul></div>
7856  *
7857  * @cfg {String} afterCls A css class to apply after the effect
7858  * @cfg {Number} duration The length of time (in seconds) that the effect should last
7859  * 
7860  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7861  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7862  *  
7863  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7864  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
7865  * effects that end with the element being visually hidden, ignored otherwise)
7866  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7867  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7868  * Element after the effect finishes.
7869  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7870  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
7871  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7872  */
7873 Ext.Fx = {
7874     
7875     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.
7876     //           this is useful for replacing switch statements (for example).
7877     switchStatements : function(key, fn, argHash){
7878         return fn.apply(this, argHash[key]);
7879     },
7880     
7881     /**
7882      * Slides the element into view.  An anchor point can be optionally passed to set the point of
7883      * origin for the slide effect.  This function automatically handles wrapping the element with
7884      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
7885      * Usage:
7886      *<pre><code>
7887 // default: slide the element in from the top
7888 el.slideIn();
7889
7890 // custom: slide the element in from the right with a 2-second duration
7891 el.slideIn('r', { duration: 2 });
7892
7893 // common config options shown with default values
7894 el.slideIn('t', {
7895     easing: 'easeOut',
7896     duration: .5
7897 });
7898 </code></pre>
7899      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7900      * @param {Object} options (optional) Object literal with any of the Fx config options
7901      * @return {Ext.Element} The Element
7902      */
7903     slideIn : function(anchor, o){ 
7904         o = getObject(o);
7905         var me = this,
7906             dom = me.dom,
7907             st = dom.style,
7908             xy,
7909             r,
7910             b,              
7911             wrap,               
7912             after,
7913             st,
7914             args, 
7915             pt,
7916             bw,
7917             bh;
7918             
7919         anchor = anchor || "t";
7920
7921         me.queueFx(o, function(){            
7922             xy = fly(dom).getXY();
7923             // fix display to visibility
7924             fly(dom).fixDisplay();            
7925             
7926             // restore values after effect
7927             r = fly(dom).getFxRestore();      
7928             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
7929             b.right = b.x + b.width;
7930             b.bottom = b.y + b.height;
7931             
7932             // fixed size for slide
7933             fly(dom).setWidth(b.width).setHeight(b.height);            
7934             
7935             // wrap if needed
7936             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
7937             
7938             st.visibility = VISIBLE;
7939             st.position = ABSOLUTE;
7940             
7941             // clear out temp styles after slide and unwrap
7942             function after(){
7943                  fly(dom).fxUnwrap(wrap, r.pos, o);
7944                  st.width = r.width;
7945                  st.height = r.height;
7946                  fly(dom).afterFx(o);
7947             }
7948             
7949             // time to calculate the positions        
7950             pt = {to: [b.x, b.y]}; 
7951             bw = {to: b.width};
7952             bh = {to: b.height};
7953                 
7954             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    
7955                 var ret = {};
7956                 fly(wrap).setWidth(ww).setHeight(wh);
7957                 if(fly(wrap)[sXY]){
7958                     fly(wrap)[sXY](sXYval);                  
7959                 }
7960                 style[s1] = style[s2] = "0";                    
7961                 if(w){
7962                     ret.width = w
7963                 };
7964                 if(h){
7965                     ret.height = h;
7966                 }
7967                 if(p){
7968                     ret.points = p;
7969                 }
7970                 return ret;
7971             };
7972
7973             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
7974                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
7975                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
7976                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
7977                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
7978                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
7979                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
7980                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
7981                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
7982                 });
7983             
7984             st.visibility = VISIBLE;
7985             fly(wrap).show();
7986
7987             arguments.callee.anim = fly(wrap).fxanim(args,
7988                 o,
7989                 MOTION,
7990                 .5,
7991                 EASEOUT, 
7992                 after);
7993         });
7994         return me;
7995     },
7996     
7997     /**
7998      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
7999      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
8000      * 'hidden') but block elements will still take up space in the document.  The element must be removed
8001      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
8002      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
8003      * Usage:
8004      *<pre><code>
8005 // default: slide the element out to the top
8006 el.slideOut();
8007
8008 // custom: slide the element out to the right with a 2-second duration
8009 el.slideOut('r', { duration: 2 });
8010
8011 // common config options shown with default values
8012 el.slideOut('t', {
8013     easing: 'easeOut',
8014     duration: .5,
8015     remove: false,
8016     useDisplay: false
8017 });
8018 </code></pre>
8019      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8020      * @param {Object} options (optional) Object literal with any of the Fx config options
8021      * @return {Ext.Element} The Element
8022      */
8023     slideOut : function(anchor, o){
8024         o = getObject(o);
8025         var me = this,
8026             dom = me.dom,
8027             st = dom.style,
8028             xy = me.getXY(),
8029             wrap,
8030             r,
8031             b,
8032             a,
8033             zero = {to: 0}; 
8034                     
8035         anchor = anchor || "t";
8036
8037         me.queueFx(o, function(){
8038             
8039             // restore values after effect
8040             r = fly(dom).getFxRestore(); 
8041             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8042             b.right = b.x + b.width;
8043             b.bottom = b.y + b.height;
8044                 
8045             // fixed size for slide   
8046             fly(dom).setWidth(b.width).setHeight(b.height);
8047
8048             // wrap if needed
8049             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8050                 
8051             st.visibility = VISIBLE;
8052             st.position = ABSOLUTE;
8053             fly(wrap).setWidth(b.width).setHeight(b.height);            
8054
8055             function after(){
8056                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                
8057                 fly(dom).fxUnwrap(wrap, r.pos, o);
8058                 st.width = r.width;
8059                 st.height = r.height;
8060                 fly(dom).afterFx(o);
8061             }            
8062             
8063             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    
8064                 var ret = {};
8065                 
8066                 style[s1] = style[s2] = "0";
8067                 ret[p1] = v1;               
8068                 if(p2){
8069                     ret[p2] = v2;               
8070                 }
8071                 if(p3){
8072                     ret[p3] = v3;
8073                 }
8074                 
8075                 return ret;
8076             };
8077             
8078             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8079                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],
8080                 l  : [st, RIGHT, TOP, WIDTH, zero],
8081                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8082                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8083                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8084                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8085                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8086                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8087             });
8088             
8089             arguments.callee.anim = fly(wrap).fxanim(a,
8090                 o,
8091                 MOTION,
8092                 .5,
8093                 EASEOUT, 
8094                 after);
8095         });
8096         return me;
8097     },
8098
8099     /**
8100      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
8101      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
8102      * The element must be removed from the DOM using the 'remove' config option if desired.
8103      * Usage:
8104      *<pre><code>
8105 // default
8106 el.puff();
8107
8108 // common config options shown with default values
8109 el.puff({
8110     easing: 'easeOut',
8111     duration: .5,
8112     remove: false,
8113     useDisplay: false
8114 });
8115 </code></pre>
8116      * @param {Object} options (optional) Object literal with any of the Fx config options
8117      * @return {Ext.Element} The Element
8118      */
8119     puff : function(o){
8120         o = getObject(o);
8121         var me = this,
8122             dom = me.dom,
8123             st = dom.style,
8124             width,
8125             height,
8126             r;
8127
8128         me.queueFx(o, function(){
8129             width = fly(dom).getWidth();
8130             height = fly(dom).getHeight();
8131             fly(dom).clearOpacity();
8132             fly(dom).show();
8133
8134             // restore values after effect
8135             r = fly(dom).getFxRestore();                   
8136             
8137             function after(){
8138                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  
8139                 fly(dom).clearOpacity();  
8140                 fly(dom).setPositioning(r.pos);
8141                 st.width = r.width;
8142                 st.height = r.height;
8143                 st.fontSize = '';
8144                 fly(dom).afterFx(o);
8145             }   
8146
8147             arguments.callee.anim = fly(dom).fxanim({
8148                     width : {to : fly(dom).adjustWidth(width * 2)},
8149                     height : {to : fly(dom).adjustHeight(height * 2)},
8150                     points : {by : [-width * .5, -height * .5]},
8151                     opacity : {to : 0},
8152                     fontSize: {to : 200, unit: "%"}
8153                 },
8154                 o,
8155                 MOTION,
8156                 .5,
8157                 EASEOUT,
8158                  after);
8159         });
8160         return me;
8161     },
8162
8163     /**
8164      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8165      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
8166      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8167      * Usage:
8168      *<pre><code>
8169 // default
8170 el.switchOff();
8171
8172 // all config options shown with default values
8173 el.switchOff({
8174     easing: 'easeIn',
8175     duration: .3,
8176     remove: false,
8177     useDisplay: false
8178 });
8179 </code></pre>
8180      * @param {Object} options (optional) Object literal with any of the Fx config options
8181      * @return {Ext.Element} The Element
8182      */
8183     switchOff : function(o){
8184         o = getObject(o);
8185         var me = this,
8186             dom = me.dom,
8187             st = dom.style,
8188             r;
8189
8190         me.queueFx(o, function(){
8191             fly(dom).clearOpacity();
8192             fly(dom).clip();
8193
8194             // restore values after effect
8195             r = fly(dom).getFxRestore();
8196                 
8197             function after(){
8198                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  
8199                 fly(dom).clearOpacity();
8200                 fly(dom).setPositioning(r.pos);
8201                 st.width = r.width;
8202                 st.height = r.height;   
8203                 fly(dom).afterFx(o);
8204             };
8205
8206             fly(dom).fxanim({opacity : {to : 0.3}}, 
8207                 NULL, 
8208                 NULL, 
8209                 .1, 
8210                 NULL, 
8211                 function(){                                 
8212                     fly(dom).clearOpacity();
8213                         (function(){                            
8214                             fly(dom).fxanim({
8215                                 height : {to : 1},
8216                                 points : {by : [0, fly(dom).getHeight() * .5]}
8217                             }, 
8218                             o, 
8219                             MOTION, 
8220                             0.3, 
8221                             'easeIn', 
8222                             after);
8223                         }).defer(100);
8224                 });
8225         });
8226         return me;
8227     },
8228
8229     /**
8230      * Highlights the Element by setting a color (applies to the background-color by default, but can be
8231      * changed using the "attr" config option) and then fading back to the original color. If no original
8232      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8233      * Usage:
8234 <pre><code>
8235 // default: highlight background to yellow
8236 el.highlight();
8237
8238 // custom: highlight foreground text to blue for 2 seconds
8239 el.highlight("0000ff", { attr: 'color', duration: 2 });
8240
8241 // common config options shown with default values
8242 el.highlight("ffff9c", {
8243     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8244     endColor: (current color) or "ffffff",
8245     easing: 'easeIn',
8246     duration: 1
8247 });
8248 </code></pre>
8249      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8250      * @param {Object} options (optional) Object literal with any of the Fx config options
8251      * @return {Ext.Element} The Element
8252      */ 
8253     highlight : function(color, o){
8254         o = getObject(o);
8255         var me = this,
8256             dom = me.dom,
8257             attr = o.attr || "backgroundColor",
8258             a = {},
8259             restore;
8260
8261         me.queueFx(o, function(){
8262             fly(dom).clearOpacity();
8263             fly(dom).show();
8264
8265             function after(){
8266                 dom.style[attr] = restore;
8267                 fly(dom).afterFx(o);
8268             }            
8269             restore = dom.style[attr];
8270             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8271             arguments.callee.anim = fly(dom).fxanim(a,
8272                 o,
8273                 'color',
8274                 1,
8275                 'easeIn', 
8276                 after);
8277         });
8278         return me;
8279     },
8280
8281    /**
8282     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8283     * Usage:
8284 <pre><code>
8285 // default: a single light blue ripple
8286 el.frame();
8287
8288 // custom: 3 red ripples lasting 3 seconds total
8289 el.frame("ff0000", 3, { duration: 3 });
8290
8291 // common config options shown with default values
8292 el.frame("C3DAF9", 1, {
8293     duration: 1 //duration of each individual ripple.
8294     // Note: Easing is not configurable and will be ignored if included
8295 });
8296 </code></pre>
8297     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8298     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8299     * @param {Object} options (optional) Object literal with any of the Fx config options
8300     * @return {Ext.Element} The Element
8301     */
8302     frame : function(color, count, o){
8303         o = getObject(o);
8304         var me = this,
8305             dom = me.dom,
8306             proxy,
8307             active;
8308
8309         me.queueFx(o, function(){
8310             color = color || '#C3DAF9';
8311             if(color.length == 6){
8312                 color = '#' + color;
8313             }            
8314             count = count || 1;
8315             fly(dom).show();
8316
8317             var xy = fly(dom).getXY(),
8318                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8319                 queue = function(){
8320                     proxy = fly(document.body || document.documentElement).createChild({
8321                         style:{
8322                             position : ABSOLUTE,
8323                             'z-index': 35000, // yee haw
8324                             border : '0px solid ' + color
8325                         }
8326                     });
8327                     return proxy.queueFx({}, animFn);
8328                 };
8329             
8330             
8331             arguments.callee.anim = {
8332                 isAnimated: true,
8333                 stop: function() {
8334                     count = 0;
8335                     proxy.stopFx();
8336                 }
8337             };
8338             
8339             function animFn(){
8340                 var scale = Ext.isBorderBox ? 2 : 1;
8341                 active = proxy.anim({
8342                     top : {from : b.y, to : b.y - 20},
8343                     left : {from : b.x, to : b.x - 20},
8344                     borderWidth : {from : 0, to : 10},
8345                     opacity : {from : 1, to : 0},
8346                     height : {from : b.height, to : b.height + 20 * scale},
8347                     width : {from : b.width, to : b.width + 20 * scale}
8348                 },{
8349                     duration: o.duration || 1,
8350                     callback: function() {
8351                         proxy.remove();
8352                         --count > 0 ? queue() : fly(dom).afterFx(o);
8353                     }
8354                 });
8355                 arguments.callee.anim = {
8356                     isAnimated: true,
8357                     stop: function(){
8358                         active.stop();
8359                     }
8360                 };
8361             };
8362             queue();
8363         });
8364         return me;
8365     },
8366
8367    /**
8368     * Creates a pause before any subsequent queued effects begin.  If there are
8369     * no effects queued after the pause it will have no effect.
8370     * Usage:
8371 <pre><code>
8372 el.pause(1);
8373 </code></pre>
8374     * @param {Number} seconds The length of time to pause (in seconds)
8375     * @return {Ext.Element} The Element
8376     */
8377     pause : function(seconds){        
8378         var dom = this.dom,
8379             t;
8380
8381         this.queueFx({}, function(){
8382             t = setTimeout(function(){
8383                 fly(dom).afterFx({});
8384             }, seconds * 1000);
8385             arguments.callee.anim = {
8386                 isAnimated: true,
8387                 stop: function(){
8388                     clearTimeout(t);
8389                     fly(dom).afterFx({});
8390                 }
8391             };
8392         });
8393         return this;
8394     },
8395
8396    /**
8397     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
8398     * using the <tt>{@link #endOpacity}</tt> config option.
8399     * Usage:
8400 <pre><code>
8401 // default: fade in from opacity 0 to 100%
8402 el.fadeIn();
8403
8404 // custom: fade in from opacity 0 to 75% over 2 seconds
8405 el.fadeIn({ endOpacity: .75, duration: 2});
8406
8407 // common config options shown with default values
8408 el.fadeIn({
8409     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8410     easing: 'easeOut',
8411     duration: .5
8412 });
8413 </code></pre>
8414     * @param {Object} options (optional) Object literal with any of the Fx config options
8415     * @return {Ext.Element} The Element
8416     */
8417     fadeIn : function(o){
8418         o = getObject(o);
8419         var me = this,
8420             dom = me.dom,
8421             to = o.endOpacity || 1;
8422         
8423         me.queueFx(o, function(){
8424             fly(dom).setOpacity(0);
8425             fly(dom).fixDisplay();
8426             dom.style.visibility = VISIBLE;
8427             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8428                 o, NULL, .5, EASEOUT, function(){
8429                 if(to == 1){
8430                     fly(dom).clearOpacity();
8431                 }
8432                 fly(dom).afterFx(o);
8433             });
8434         });
8435         return me;
8436     },
8437
8438    /**
8439     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
8440     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
8441     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8442     * Usage:
8443 <pre><code>
8444 // default: fade out from the element's current opacity to 0
8445 el.fadeOut();
8446
8447 // custom: fade out from the element's current opacity to 25% over 2 seconds
8448 el.fadeOut({ endOpacity: .25, duration: 2});
8449
8450 // common config options shown with default values
8451 el.fadeOut({
8452     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8453     easing: 'easeOut',
8454     duration: .5,
8455     remove: false,
8456     useDisplay: false
8457 });
8458 </code></pre>
8459     * @param {Object} options (optional) Object literal with any of the Fx config options
8460     * @return {Ext.Element} The Element
8461     */
8462     fadeOut : function(o){
8463         o = getObject(o);
8464         var me = this,
8465             dom = me.dom,
8466             style = dom.style,
8467             to = o.endOpacity || 0;         
8468         
8469         me.queueFx(o, function(){  
8470             arguments.callee.anim = fly(dom).fxanim({ 
8471                 opacity : {to : to}},
8472                 o, 
8473                 NULL, 
8474                 .5, 
8475                 EASEOUT, 
8476                 function(){
8477                     if(to == 0){
8478                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? 
8479                             style.display = "none" :
8480                             style.visibility = HIDDEN;
8481                             
8482                         fly(dom).clearOpacity();
8483                     }
8484                     fly(dom).afterFx(o);
8485             });
8486         });
8487         return me;
8488     },
8489
8490    /**
8491     * Animates the transition of an element's dimensions from a starting height/width
8492     * to an ending height/width.  This method is a convenience implementation of {@link shift}.
8493     * Usage:
8494 <pre><code>
8495 // change height and width to 100x100 pixels
8496 el.scale(100, 100);
8497
8498 // common config options shown with default values.  The height and width will default to
8499 // the element&#39;s existing values if passed as null.
8500 el.scale(
8501     [element&#39;s width],
8502     [element&#39;s height], {
8503         easing: 'easeOut',
8504         duration: .35
8505     }
8506 );
8507 </code></pre>
8508     * @param {Number} width  The new width (pass undefined to keep the original width)
8509     * @param {Number} height  The new height (pass undefined to keep the original height)
8510     * @param {Object} options (optional) Object literal with any of the Fx config options
8511     * @return {Ext.Element} The Element
8512     */
8513     scale : function(w, h, o){
8514         this.shift(Ext.apply({}, o, {
8515             width: w,
8516             height: h
8517         }));
8518         return this;
8519     },
8520
8521    /**
8522     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8523     * Any of these properties not specified in the config object will not be changed.  This effect 
8524     * requires that at least one new dimension, position or opacity setting must be passed in on
8525     * the config object in order for the function to have any effect.
8526     * Usage:
8527 <pre><code>
8528 // slide the element horizontally to x position 200 while changing the height and opacity
8529 el.shift({ x: 200, height: 50, opacity: .8 });
8530
8531 // common config options shown with default values.
8532 el.shift({
8533     width: [element&#39;s width],
8534     height: [element&#39;s height],
8535     x: [element&#39;s x position],
8536     y: [element&#39;s y position],
8537     opacity: [element&#39;s opacity],
8538     easing: 'easeOut',
8539     duration: .35
8540 });
8541 </code></pre>
8542     * @param {Object} options  Object literal with any of the Fx config options
8543     * @return {Ext.Element} The Element
8544     */
8545     shift : function(o){
8546         o = getObject(o);
8547         var dom = this.dom,
8548             a = {};
8549                 
8550         this.queueFx(o, function(){
8551             for (var prop in o) {
8552                 if (o[prop] != UNDEFINED) {                                                 
8553                     a[prop] = {to : o[prop]};                   
8554                 }
8555             } 
8556             
8557             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8558             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   
8559             
8560             if (a.x || a.y || a.xy) {
8561                 a.points = a.xy || 
8562                            {to : [ a.x ? a.x.to : fly(dom).getX(),
8563                                    a.y ? a.y.to : fly(dom).getY()]};                  
8564             }
8565
8566             arguments.callee.anim = fly(dom).fxanim(a,
8567                 o, 
8568                 MOTION, 
8569                 .35, 
8570                 EASEOUT, 
8571                 function(){
8572                     fly(dom).afterFx(o);
8573                 });
8574         });
8575         return this;
8576     },
8577
8578     /**
8579      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
8580      * ending point of the effect.
8581      * Usage:
8582      *<pre><code>
8583 // default: slide the element downward while fading out
8584 el.ghost();
8585
8586 // custom: slide the element out to the right with a 2-second duration
8587 el.ghost('r', { duration: 2 });
8588
8589 // common config options shown with default values
8590 el.ghost('b', {
8591     easing: 'easeOut',
8592     duration: .5,
8593     remove: false,
8594     useDisplay: false
8595 });
8596 </code></pre>
8597      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8598      * @param {Object} options (optional) Object literal with any of the Fx config options
8599      * @return {Ext.Element} The Element
8600      */
8601     ghost : function(anchor, o){
8602         o = getObject(o);
8603         var me = this,
8604             dom = me.dom,
8605             st = dom.style,
8606             a = {opacity: {to: 0}, points: {}},
8607             pt = a.points,
8608             r,
8609             w,
8610             h;
8611             
8612         anchor = anchor || "b";
8613
8614         me.queueFx(o, function(){
8615             // restore values after effect
8616             r = fly(dom).getFxRestore();
8617             w = fly(dom).getWidth();
8618             h = fly(dom).getHeight();
8619             
8620             function after(){
8621                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   
8622                 fly(dom).clearOpacity();
8623                 fly(dom).setPositioning(r.pos);
8624                 st.width = r.width;
8625                 st.height = r.height;
8626                 fly(dom).afterFx(o);
8627             }
8628                 
8629             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8630                t  : [0, -h],
8631                l  : [-w, 0],
8632                r  : [w, 0],
8633                b  : [0, h],
8634                tl : [-w, -h],
8635                bl : [-w, h],
8636                br : [w, h],
8637                tr : [w, -h] 
8638             });
8639                 
8640             arguments.callee.anim = fly(dom).fxanim(a,
8641                 o,
8642                 MOTION,
8643                 .5,
8644                 EASEOUT, after);
8645         });
8646         return me;
8647     },
8648
8649     /**
8650      * Ensures that all effects queued after syncFx is called on the element are
8651      * run concurrently.  This is the opposite of {@link #sequenceFx}.
8652      * @return {Ext.Element} The Element
8653      */
8654     syncFx : function(){
8655         var me = this;
8656         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8657             block : FALSE,
8658             concurrent : TRUE,
8659             stopFx : FALSE
8660         });
8661         return me;
8662     },
8663
8664     /**
8665      * Ensures that all effects queued after sequenceFx is called on the element are
8666      * run in sequence.  This is the opposite of {@link #syncFx}.
8667      * @return {Ext.Element} The Element
8668      */
8669     sequenceFx : function(){
8670         var me = this;
8671         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8672             block : FALSE,
8673             concurrent : FALSE,
8674             stopFx : FALSE
8675         });
8676         return me;
8677     },
8678
8679     /* @private */
8680     nextFx : function(){        
8681         var ef = getQueue(this.dom.id)[0];
8682         if(ef){
8683             ef.call(this);
8684         }
8685     },
8686
8687     /**
8688      * Returns true if the element has any effects actively running or queued, else returns false.
8689      * @return {Boolean} True if element has active effects, else false
8690      */
8691     hasActiveFx : function(){
8692         return getQueue(this.dom.id)[0];
8693     },
8694
8695     /**
8696      * Stops any running effects and clears the element's internal effects queue if it contains
8697      * any additional effects that haven't started yet.
8698      * @return {Ext.Element} The Element
8699      */
8700     stopFx : function(finish){
8701         var me = this,
8702             id = me.dom.id;
8703         if(me.hasActiveFx()){
8704             var cur = getQueue(id)[0];
8705             if(cur && cur.anim){
8706                 if(cur.anim.isAnimated){
8707                     setQueue(id, [cur]); //clear
8708                     cur.anim.stop(finish !== undefined ? finish : TRUE);
8709                 }else{
8710                     setQueue(id, []);
8711                 }
8712             }
8713         }
8714         return me;
8715     },
8716
8717     /* @private */
8718     beforeFx : function(o){
8719         if(this.hasActiveFx() && !o.concurrent){
8720            if(o.stopFx){
8721                this.stopFx();
8722                return TRUE;
8723            }
8724            return FALSE;
8725         }
8726         return TRUE;
8727     },
8728
8729     /**
8730      * Returns true if the element is currently blocking so that no other effect can be queued
8731      * until this effect is finished, else returns false if blocking is not set.  This is commonly
8732      * used to ensure that an effect initiated by a user action runs to completion prior to the
8733      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8734      * @return {Boolean} True if blocking, else false
8735      */
8736     hasFxBlock : function(){
8737         var q = getQueue(this.dom.id);
8738         return q && q[0] && q[0].block;
8739     },
8740
8741     /* @private */
8742     queueFx : function(o, fn){
8743         var me = fly(this.dom);
8744         if(!me.hasFxBlock()){
8745             Ext.applyIf(o, me.fxDefaults);
8746             if(!o.concurrent){
8747                 var run = me.beforeFx(o);
8748                 fn.block = o.block;
8749                 getQueue(me.dom.id).push(fn);
8750                 if(run){
8751                     me.nextFx();
8752                 }
8753             }else{
8754                 fn.call(me);
8755             }
8756         }
8757         return me;
8758     },
8759
8760     /* @private */
8761     fxWrap : function(pos, o, vis){ 
8762         var dom = this.dom,
8763             wrap,
8764             wrapXY;
8765         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            
8766             if(o.fixPosition){
8767                 wrapXY = fly(dom).getXY();
8768             }
8769             var div = document.createElement("div");
8770             div.style.visibility = vis;
8771             wrap = dom.parentNode.insertBefore(div, dom);
8772             fly(wrap).setPositioning(pos);
8773             if(fly(wrap).isStyle(POSITION, "static")){
8774                 fly(wrap).position("relative");
8775             }
8776             fly(dom).clearPositioning('auto');
8777             fly(wrap).clip();
8778             wrap.appendChild(dom);
8779             if(wrapXY){
8780                 fly(wrap).setXY(wrapXY);
8781             }
8782         }
8783         return wrap;
8784     },
8785
8786     /* @private */
8787     fxUnwrap : function(wrap, pos, o){      
8788         var dom = this.dom;
8789         fly(dom).clearPositioning();
8790         fly(dom).setPositioning(pos);
8791         if(!o.wrap){
8792             var pn = fly(wrap).dom.parentNode;
8793             pn.insertBefore(dom, wrap); 
8794             fly(wrap).remove();
8795         }
8796     },
8797
8798     /* @private */
8799     getFxRestore : function(){
8800         var st = this.dom.style;
8801         return {pos: this.getPositioning(), width: st.width, height : st.height};
8802     },
8803
8804     /* @private */
8805     afterFx : function(o){
8806         var dom = this.dom,
8807             id = dom.id;
8808         if(o.afterStyle){
8809             fly(dom).setStyle(o.afterStyle);            
8810         }
8811         if(o.afterCls){
8812             fly(dom).addClass(o.afterCls);
8813         }
8814         if(o.remove == TRUE){
8815             fly(dom).remove();
8816         }
8817         if(o.callback){
8818             o.callback.call(o.scope, fly(dom));
8819         }
8820         if(!o.concurrent){
8821             getQueue(id).shift();
8822             fly(dom).nextFx();
8823         }
8824     },
8825
8826     /* @private */
8827     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8828         animType = animType || 'run';
8829         opt = opt || {};
8830         var anim = Ext.lib.Anim[animType](
8831                 this.dom, 
8832                 args,
8833                 (opt.duration || defaultDur) || .35,
8834                 (opt.easing || defaultEase) || EASEOUT,
8835                 cb,            
8836                 this
8837             );
8838         opt.anim = anim;
8839         return anim;
8840     }
8841 };
8842
8843 // backwards compat
8844 Ext.Fx.resize = Ext.Fx.scale;
8845
8846 //When included, Ext.Fx is automatically applied to Element so that all basic
8847 //effects are available directly via the Element API
8848 Ext.Element.addMethods(Ext.Fx);
8849 })();
8850 /**
8851  * @class Ext.CompositeElementLite
8852  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8853  * members, or to perform collective actions upon the whole set.</p>
8854  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8855  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8856  * Example:<pre><code>
8857 var els = Ext.select("#some-el div.some-class");
8858 // or select directly from an existing element
8859 var el = Ext.get('some-el');
8860 el.select('div.some-class');
8861
8862 els.setWidth(100); // all elements become 100 width
8863 els.hide(true); // all elements fade out and hide
8864 // or
8865 els.setWidth(100).hide(true);
8866 </code>
8867  */
8868 Ext.CompositeElementLite = function(els, root){
8869     /**
8870      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8871      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8872      * to augment the capabilities of the CompositeElementLite class may use it when adding
8873      * methods to the class.</p>
8874      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8875      * following siblings of selected elements, the code would be</p><code><pre>
8876 Ext.override(Ext.CompositeElementLite, {
8877     nextAll: function() {
8878         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8879
8880 //      Loop through all elements in this Composite, accumulating
8881 //      an Array of all siblings.
8882         for (i = 0; i < l; i++) {
8883             for (n = els[i].nextSibling; n; n = n.nextSibling) {
8884                 r[++ri] = n;
8885             }
8886         }
8887
8888 //      Add all found siblings to this Composite
8889         return this.add(r);
8890     }
8891 });</pre></code>
8892      * @type Array
8893      * @property elements
8894      */
8895     this.elements = [];
8896     this.add(els, root);
8897     this.el = new Ext.Element.Flyweight();
8898 };
8899
8900 Ext.CompositeElementLite.prototype = {
8901     isComposite: true,    
8902     
8903     // private
8904     getElement : function(el){
8905         // Set the shared flyweight dom property to the current element
8906         var e = this.el;
8907         e.dom = el;
8908         e.id = el.id;
8909         return e;
8910     },
8911     
8912     // private
8913     transformElement : function(el){
8914         return Ext.getDom(el);
8915     },
8916     
8917     /**
8918      * Returns the number of elements in this Composite.
8919      * @return Number
8920      */
8921     getCount : function(){
8922         return this.elements.length;
8923     },    
8924     /**
8925      * Adds elements to this Composite object.
8926      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
8927      * @return {CompositeElement} This Composite object.
8928      */
8929     add : function(els, root){
8930         var me = this,
8931             elements = me.elements;
8932         if(!els){
8933             return this;
8934         }
8935         if(Ext.isString(els)){
8936             els = Ext.Element.selectorFunction(els, root);
8937         }else if(els.isComposite){
8938             els = els.elements;
8939         }else if(!Ext.isIterable(els)){
8940             els = [els];
8941         }
8942         
8943         for(var i = 0, len = els.length; i < len; ++i){
8944             elements.push(me.transformElement(els[i]));
8945         }
8946         return me;
8947     },
8948     
8949     invoke : function(fn, args){
8950         var me = this,
8951             els = me.elements,
8952             len = els.length, 
8953             e, 
8954             i;
8955             
8956         for(i = 0; i < len; i++) {
8957             e = els[i];
8958             if(e){
8959                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
8960             }
8961         }
8962         return me;
8963     },
8964     /**
8965      * Returns a flyweight Element of the dom element object at the specified index
8966      * @param {Number} index
8967      * @return {Ext.Element}
8968      */
8969     item : function(index){
8970         var me = this,
8971             el = me.elements[index],
8972             out = null;
8973
8974         if(el){
8975             out = me.getElement(el);
8976         }
8977         return out;
8978     },
8979
8980     // fixes scope with flyweight
8981     addListener : function(eventName, handler, scope, opt){
8982         var els = this.elements,
8983             len = els.length,
8984             i, e;
8985         
8986         for(i = 0; i<len; i++) {
8987             e = els[i];
8988             if(e) {
8989                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
8990             }
8991         }
8992         return this;
8993     },
8994     /**
8995      * <p>Calls the passed function for each element in this composite.</p>
8996      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
8997      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
8998      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
8999      * a reference to the dom node, use el.dom.</b></div></li>
9000      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9001      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9002      * </ul>
9003      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9004      * @return {CompositeElement} this
9005      */
9006     each : function(fn, scope){       
9007         var me = this,
9008             els = me.elements,
9009             len = els.length,
9010             i, e;
9011         
9012         for(i = 0; i<len; i++) {
9013             e = els[i];
9014             if(e){
9015                 e = this.getElement(e);
9016                 if(fn.call(scope || e, e, me, i) === false){
9017                     break;
9018                 }
9019             }
9020         }
9021         return me;
9022     },
9023     
9024     /**
9025     * Clears this Composite and adds the elements passed.
9026     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9027     * @return {CompositeElement} this
9028     */
9029     fill : function(els){
9030         var me = this;
9031         me.elements = [];
9032         me.add(els);
9033         return me;
9034     },
9035     
9036     /**
9037      * Filters this composite to only elements that match the passed selector.
9038      * @param {String/Function} selector A string CSS selector or a comparison function.
9039      * The comparison function will be called with the following arguments:<ul>
9040      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9041      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9042      * </ul>
9043      * @return {CompositeElement} this
9044      */
9045     filter : function(selector){
9046         var els = [],
9047             me = this,
9048             elements = me.elements,
9049             fn = Ext.isFunction(selector) ? selector
9050                 : function(el){
9051                     return el.is(selector);
9052                 };
9053                 
9054         
9055         me.each(function(el, self, i){
9056             if(fn(el, i) !== false){
9057                 els[els.length] = me.transformElement(el);
9058             }
9059         });
9060         me.elements = els;
9061         return me;
9062     },
9063     
9064     /**
9065      * Find the index of the passed element within the composite collection.
9066      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9067      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9068      */
9069     indexOf : function(el){
9070         return this.elements.indexOf(this.transformElement(el));
9071     },
9072     
9073     /**
9074     * Replaces the specified element with the passed element.
9075     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9076     * to replace.
9077     * @param {Mixed} replacement The id of an element or the Element itself.
9078     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9079     * @return {CompositeElement} this
9080     */    
9081     replaceElement : function(el, replacement, domReplace){
9082         var index = !isNaN(el) ? el : this.indexOf(el),
9083             d;
9084         if(index > -1){
9085             replacement = Ext.getDom(replacement);
9086             if(domReplace){
9087                 d = this.elements[index];
9088                 d.parentNode.insertBefore(replacement, d);
9089                 Ext.removeNode(d);
9090             }
9091             this.elements.splice(index, 1, replacement);
9092         }
9093         return this;
9094     },
9095     
9096     /**
9097      * Removes all elements.
9098      */
9099     clear : function(){
9100         this.elements = [];
9101     }
9102 };
9103
9104 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9105
9106 (function(){
9107 var fnName,
9108     ElProto = Ext.Element.prototype,
9109     CelProto = Ext.CompositeElementLite.prototype;
9110     
9111 for(fnName in ElProto){
9112     if(Ext.isFunction(ElProto[fnName])){
9113         (function(fnName){ 
9114             CelProto[fnName] = CelProto[fnName] || function(){
9115                 return this.invoke(fnName, arguments);
9116             };
9117         }).call(CelProto, fnName);
9118         
9119     }
9120 }
9121 })();
9122
9123 if(Ext.DomQuery){
9124     Ext.Element.selectorFunction = Ext.DomQuery.select;
9125
9126
9127 /**
9128  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9129  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9130  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9131  * @param {String/Array} selector The CSS selector or an array of elements
9132  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9133  * @return {CompositeElementLite/CompositeElement}
9134  * @member Ext.Element
9135  * @method select
9136  */
9137 Ext.Element.select = function(selector, root){
9138     var els;
9139     if(typeof selector == "string"){
9140         els = Ext.Element.selectorFunction(selector, root);
9141     }else if(selector.length !== undefined){
9142         els = selector;
9143     }else{
9144         throw "Invalid selector";
9145     }
9146     return new Ext.CompositeElementLite(els);
9147 };
9148 /**
9149  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9150  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9151  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9152  * @param {String/Array} selector The CSS selector or an array of elements
9153  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9154  * @return {CompositeElementLite/CompositeElement}
9155  * @member Ext
9156  * @method select
9157  */
9158 Ext.select = Ext.Element.select;/**
9159  * @class Ext.CompositeElementLite
9160  */
9161 Ext.apply(Ext.CompositeElementLite.prototype, { 
9162         addElements : function(els, root){
9163         if(!els){
9164             return this;
9165         }
9166         if(typeof els == "string"){
9167             els = Ext.Element.selectorFunction(els, root);
9168         }
9169         var yels = this.elements;        
9170             Ext.each(els, function(e) {
9171                 yels.push(Ext.get(e));
9172         });
9173         return this;
9174     },
9175     
9176     /**
9177      * Returns the first Element
9178      * @return {Ext.Element}
9179      */
9180     first : function(){
9181         return this.item(0);
9182     },   
9183     
9184     /**
9185      * Returns the last Element
9186      * @return {Ext.Element}
9187      */
9188     last : function(){
9189         return this.item(this.getCount()-1);
9190     },
9191     
9192     /**
9193      * Returns true if this composite contains the passed element
9194      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9195      * @return Boolean
9196      */
9197     contains : function(el){
9198         return this.indexOf(el) != -1;
9199     },
9200     
9201     /**
9202     * Removes the specified element(s).
9203     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9204     * or an array of any of those.
9205     * @param {Boolean} removeDom (optional) True to also remove the element from the document
9206     * @return {CompositeElement} this
9207     */
9208     removeElement : function(keys, removeDom){
9209         var me = this,
9210                 els = this.elements,        
9211                 el;             
9212             Ext.each(keys, function(val){
9213                     if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9214                         if(removeDom){
9215                     if(el.dom){
9216                         el.remove();
9217                     }else{
9218                         Ext.removeNode(el);
9219                     }
9220                 }
9221                         els.splice(val, 1);                     
9222                         }
9223             });
9224         return this;
9225     }    
9226 });
9227 /**
9228  * @class Ext.CompositeElement
9229  * @extends Ext.CompositeElementLite
9230  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9231  * members, or to perform collective actions upon the whole set.</p>
9232  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9233  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9234  * <p>All methods return <i>this</i> and can be chained.</p>
9235  * Usage:
9236 <pre><code>
9237 var els = Ext.select("#some-el div.some-class", true);
9238 // or select directly from an existing element
9239 var el = Ext.get('some-el');
9240 el.select('div.some-class', true);
9241
9242 els.setWidth(100); // all elements become 100 width
9243 els.hide(true); // all elements fade out and hide
9244 // or
9245 els.setWidth(100).hide(true);
9246 </code></pre>
9247  */
9248 Ext.CompositeElement = function(els, root){
9249     this.elements = [];
9250     this.add(els, root);
9251 };
9252
9253 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {
9254     
9255     // private
9256     getElement : function(el){
9257         // In this case just return it, since we already have a reference to it
9258         return el;
9259     },
9260     
9261     // private
9262     transformElement : function(el){
9263         return Ext.get(el);
9264     }
9265
9266     /**
9267     * Adds elements to this composite.
9268     * @param {String/Array} els A string CSS selector, an array of elements or an element
9269     * @return {CompositeElement} this
9270     */
9271
9272     /**
9273      * Returns the Element object at the specified index
9274      * @param {Number} index
9275      * @return {Ext.Element}
9276      */
9277
9278     /**
9279      * Iterates each <code>element</code> in this <code>composite</code>
9280      * calling the supplied function using {@link Ext#each}.
9281      * @param {Function} fn The function to be called with each
9282      * <code>element</code>. If the supplied function returns <tt>false</tt>,
9283      * iteration stops. This function is called with the following arguments:
9284      * <div class="mdetail-params"><ul>
9285      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9286      * in the <code>composite</code></div></li>
9287      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9288      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9289      * </ul></div>
9290      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9291      * Defaults to the <code>element</code> at the current <code>index</code>
9292      * within the composite.
9293      * @return {CompositeElement} this
9294      */
9295 });
9296
9297 /**
9298  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9299  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9300  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9301  * @param {String/Array} selector The CSS selector or an array of elements
9302  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9303  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9304  * @return {CompositeElementLite/CompositeElement}
9305  * @member Ext.Element
9306  * @method select
9307  */
9308 Ext.Element.select = function(selector, unique, root){
9309     var els;
9310     if(typeof selector == "string"){
9311         els = Ext.Element.selectorFunction(selector, root);
9312     }else if(selector.length !== undefined){
9313         els = selector;
9314     }else{
9315         throw "Invalid selector";
9316     }
9317
9318     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9319 };
9320
9321 /**
9322  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9323  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9324  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9325  * @param {String/Array} selector The CSS selector or an array of elements
9326  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9327  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9328  * @return {CompositeElementLite/CompositeElement}
9329  * @member Ext.Element
9330  * @method select
9331  */
9332 Ext.select = Ext.Element.select;(function(){
9333     var BEFOREREQUEST = "beforerequest",
9334         REQUESTCOMPLETE = "requestcomplete",
9335         REQUESTEXCEPTION = "requestexception",
9336         UNDEFINED = undefined,
9337         LOAD = 'load',
9338         POST = 'POST',
9339         GET = 'GET',
9340         WINDOW = window;
9341
9342     /**
9343      * @class Ext.data.Connection
9344      * @extends Ext.util.Observable
9345      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9346      * either to a configured URL, or to a URL specified at request time.</p>
9347      * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9348      * the server will be available to the statement immediately following the {@link #request} call.
9349      * To process returned data, use a
9350      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9351      * in the request options object,
9352      * or an {@link #requestcomplete event listener}.</p>
9353      * <p><h3>File Uploads</h3><a href="#request-option-isUpload" ext:member="request-option-isUpload" ext:cls="Ext.data.Connection">File uploads</a> are not performed using normal "Ajax" techniques, that
9354      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9355      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
9356      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9357      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9358      * but removed after the return data has been gathered.</p>
9359      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9360      * server is using JSON to send the return object, then the
9361      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9362      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9363      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9364      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
9365      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9366      * is created containing a <tt>responseText</tt> property in order to conform to the
9367      * requirements of event handlers and callbacks.</p>
9368      * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
9369      * and some server technologies (notably JEE) may require some custom processing in order to
9370      * retrieve parameter names and parameter values from the packet content.</p>
9371      * @constructor
9372      * @param {Object} config a configuration object.
9373      */
9374     Ext.data.Connection = function(config){
9375         Ext.apply(this, config);
9376         this.addEvents(
9377             /**
9378              * @event beforerequest
9379              * Fires before a network request is made to retrieve a data object.
9380              * @param {Connection} conn This Connection object.
9381              * @param {Object} options The options config object passed to the {@link #request} method.
9382              */
9383             BEFOREREQUEST,
9384             /**
9385              * @event requestcomplete
9386              * Fires if the request was successfully completed.
9387              * @param {Connection} conn This Connection object.
9388              * @param {Object} response The XHR object containing the response data.
9389              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9390              * for details.
9391              * @param {Object} options The options config object passed to the {@link #request} method.
9392              */
9393             REQUESTCOMPLETE,
9394             /**
9395              * @event requestexception
9396              * Fires if an error HTTP status was returned from the server.
9397              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9398              * for details of HTTP status codes.
9399              * @param {Connection} conn This Connection object.
9400              * @param {Object} response The XHR object containing the response data.
9401              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9402              * for details.
9403              * @param {Object} options The options config object passed to the {@link #request} method.
9404              */
9405             REQUESTEXCEPTION
9406         );
9407         Ext.data.Connection.superclass.constructor.call(this);
9408     };
9409
9410     Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9411         /**
9412          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9413          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9414          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9415          */
9416         /**
9417          * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9418          * extra parameters to each request made by this object. (defaults to undefined)
9419          */
9420         /**
9421          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9422          *  to each request made by this object. (defaults to undefined)
9423          */
9424         /**
9425          * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9426          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9427          * otherwise, GET will be used.)
9428          */
9429         /**
9430          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9431          */
9432         timeout : 30000,
9433         /**
9434          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9435          * @type Boolean
9436          */
9437         autoAbort:false,
9438
9439         /**
9440          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9441          * @type Boolean
9442          */
9443         disableCaching: true,
9444
9445         /**
9446          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9447          * through a cache buster. Defaults to '_dc'
9448          * @type String
9449          */
9450         disableCachingParam: '_dc',
9451
9452         /**
9453          * <p>Sends an HTTP request to a remote server.</p>
9454          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9455          * return before the response has been received. Process any returned data
9456          * in a callback function.</p>
9457          * <pre><code>
9458 Ext.Ajax.request({
9459    url: 'ajax_demo/sample.json',
9460    success: function(response, opts) {
9461       var obj = Ext.decode(response.responseText);
9462       console.dir(obj);
9463    },
9464    failure: function(response, opts) {
9465       console.log('server-side failure with status code ' + response.status);
9466    }
9467 });
9468          * </code></pre>
9469          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9470          * @param {Object} options An object which may contain the following properties:<ul>
9471          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9472          * which to send the request, or a function to call which returns a URL string. The scope of the
9473          * function is specified by the <tt>scope</tt> option. Defaults to the configured
9474          * <tt>{@link #url}</tt>.</div></li>
9475          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9476          * An object containing properties which are used as parameters to the
9477          * request, a url encoded string or a function to call to get either. The scope of the function
9478          * is specified by the <tt>scope</tt> option.</div></li>
9479          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9480          * for the request. Defaults to the configured method, or if no method was configured,
9481          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
9482          * the method name is case-sensitive and should be all caps.</div></li>
9483          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9484          * function to be called upon receipt of the HTTP response. The callback is
9485          * called regardless of success or failure and is passed the following
9486          * parameters:<ul>
9487          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9488          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9489          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9490          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9491          * accessing elements of the response.</div></li>
9492          * </ul></div></li>
9493          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9494          * to be called upon success of the request. The callback is passed the following
9495          * parameters:<ul>
9496          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9497          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9498          * </ul></div></li>
9499          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9500          * to be called upon failure of the request. The callback is passed the
9501          * following parameters:<ul>
9502          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9503          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9504          * </ul></div></li>
9505          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9506          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9507          * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9508          * Defaults to the browser window.</div></li>
9509          * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
9510          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
9511          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
9512          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9513          * with the <tt>form</tt> option</b>.
9514          * <p>True if the form object is a file upload (will be set automatically if the form was
9515          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9516          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9517          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9518          * DOM <tt>&lt;form></tt> element temporarily modified to have its
9519          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9520          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9521          * but removed after the return data has been gathered.</p>
9522          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9523          * server is using JSON to send the return object, then the
9524          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9525          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9526          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9527          * is created containing a <tt>responseText</tt> property in order to conform to the
9528          * requirements of event handlers and callbacks.</p>
9529          * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
9530          * and some server technologies (notably JEE) may require some custom processing in order to
9531          * retrieve parameter names and parameter values from the packet content.</p>
9532          * </div></li>
9533          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9534          * headers to set for the request.</div></li>
9535          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9536          * to use for the post. Note: This will be used instead of params for the post
9537          * data. Any params will be appended to the URL.</div></li>
9538          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9539          * data to use as the post. Note: This will be used instead of params for the post
9540          * data. Any params will be appended to the URL.</div></li>
9541          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9542          * to add a unique cache-buster param to GET requests.</div></li>
9543          * </ul></p>
9544          * <p>The options object may also contain any other property which might be needed to perform
9545          * postprocessing in a callback because it is passed to callback functions.</p>
9546          * @return {Number} transactionId The id of the server transaction. This may be used
9547          * to cancel the request.
9548          */
9549         request : function(o){
9550             var me = this;
9551             if(me.fireEvent(BEFOREREQUEST, me, o)){
9552                 if (o.el) {
9553                     if(!Ext.isEmpty(o.indicatorText)){
9554                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9555                     }
9556                     if(me.indicatorText) {
9557                         Ext.getDom(o.el).innerHTML = me.indicatorText;
9558                     }
9559                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9560                         Ext.getDom(o.el).innerHTML = response.responseText;
9561                     });
9562                 }
9563
9564                 var p = o.params,
9565                     url = o.url || me.url,
9566                     method,
9567                     cb = {success: me.handleResponse,
9568                           failure: me.handleFailure,
9569                           scope: me,
9570                           argument: {options: o},
9571                           timeout : o.timeout || me.timeout
9572                     },
9573                     form,
9574                     serForm;
9575
9576
9577                 if (Ext.isFunction(p)) {
9578                     p = p.call(o.scope||WINDOW, o);
9579                 }
9580
9581                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9582
9583                 if (Ext.isFunction(url)) {
9584                     url = url.call(o.scope || WINDOW, o);
9585                 }
9586
9587                 if((form = Ext.getDom(o.form))){
9588                     url = url || form.action;
9589                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9590                          return me.doFormUpload.call(me, o, p, url);
9591                      }
9592                     serForm = Ext.lib.Ajax.serializeForm(form);
9593                     p = p ? (p + '&' + serForm) : serForm;
9594                 }
9595
9596                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9597
9598                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9599                     var dcp = o.disableCachingParam || me.disableCachingParam;
9600                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9601                 }
9602
9603                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9604
9605                 if(o.autoAbort === true || me.autoAbort) {
9606                     me.abort();
9607                 }
9608
9609                 if((method == GET || o.xmlData || o.jsonData) && p){
9610                     url = Ext.urlAppend(url, p);
9611                     p = '';
9612                 }
9613                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9614             }else{
9615                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9616             }
9617         },
9618
9619         /**
9620          * Determine whether this object has a request outstanding.
9621          * @param {Number} transactionId (Optional) defaults to the last transaction
9622          * @return {Boolean} True if there is an outstanding request.
9623          */
9624         isLoading : function(transId){
9625             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9626         },
9627
9628         /**
9629          * Aborts any outstanding request.
9630          * @param {Number} transactionId (Optional) defaults to the last transaction
9631          */
9632         abort : function(transId){
9633             if(transId || this.isLoading()){
9634                 Ext.lib.Ajax.abort(transId || this.transId);
9635             }
9636         },
9637
9638         // private
9639         handleResponse : function(response){
9640             this.transId = false;
9641             var options = response.argument.options;
9642             response.argument = options ? options.argument : null;
9643             this.fireEvent(REQUESTCOMPLETE, this, response, options);
9644             if(options.success){
9645                 options.success.call(options.scope, response, options);
9646             }
9647             if(options.callback){
9648                 options.callback.call(options.scope, options, true, response);
9649             }
9650         },
9651
9652         // private
9653         handleFailure : function(response, e){
9654             this.transId = false;
9655             var options = response.argument.options;
9656             response.argument = options ? options.argument : null;
9657             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9658             if(options.failure){
9659                 options.failure.call(options.scope, response, options);
9660             }
9661             if(options.callback){
9662                 options.callback.call(options.scope, options, false, response);
9663             }
9664         },
9665
9666         // private
9667         doFormUpload : function(o, ps, url){
9668             var id = Ext.id(),
9669                 doc = document,
9670                 frame = doc.createElement('iframe'),
9671                 form = Ext.getDom(o.form),
9672                 hiddens = [],
9673                 hd,
9674                 encoding = 'multipart/form-data',
9675                 buf = {
9676                     target: form.target,
9677                     method: form.method,
9678                     encoding: form.encoding,
9679                     enctype: form.enctype,
9680                     action: form.action
9681                 };
9682
9683             Ext.fly(frame).set({
9684                 id: id,
9685                 name: id,
9686                 cls: 'x-hidden'
9687
9688             });
9689
9690             doc.body.appendChild(frame);
9691
9692             //Reset the Frame to neutral domain
9693             Ext.fly(frame).set({
9694                src : Ext.SSL_SECURE_URL
9695             });
9696
9697             // This is required so that IE doesn't pop the response up in a new window.
9698             if(Ext.isIE){
9699                document.frames[id].name = id;
9700             }
9701
9702
9703             Ext.fly(form).set({
9704                 target: id,
9705                 method: POST,
9706                 enctype: encoding,
9707                 encoding: encoding,
9708                 action: url || buf.action
9709             });
9710
9711             // add dynamic params
9712             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9713                 hd = doc.createElement('input');
9714                 Ext.fly(hd).set({
9715                     type: 'hidden',
9716                     value: v,
9717                     name: k
9718                 });
9719                 form.appendChild(hd);
9720                 hiddens.push(hd);
9721             });
9722
9723             function cb(){
9724                 var me = this,
9725                     // bogus response object
9726                     r = {responseText : '',
9727                          responseXML : null,
9728                          argument : o.argument},
9729                     doc,
9730                     firstChild;
9731
9732                 try{
9733                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9734                     if(doc){
9735                         if(doc.body){
9736                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9737                                 r.responseText = firstChild.value;
9738                             }else{
9739                                 r.responseText = doc.body.innerHTML;
9740                             }
9741                         }
9742                         //in IE the document may still have a body even if returns XML.
9743                         r.responseXML = doc.XMLDocument || doc;
9744                     }
9745                 }
9746                 catch(e) {}
9747
9748                 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9749
9750                 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9751
9752                 function runCallback(fn, scope, args){
9753                     if(Ext.isFunction(fn)){
9754                         fn.apply(scope, args);
9755                     }
9756                 }
9757
9758                 runCallback(o.success, o.scope, [r, o]);
9759                 runCallback(o.callback, o.scope, [o, true, r]);
9760
9761                 if(!me.debugUploads){
9762                     setTimeout(function(){Ext.removeNode(frame);}, 100);
9763                 }
9764             }
9765
9766             Ext.EventManager.on(frame, LOAD, cb, this);
9767             form.submit();
9768
9769             Ext.fly(form).set(buf);
9770             Ext.each(hiddens, function(h) {
9771                 Ext.removeNode(h);
9772             });
9773         }
9774     });
9775 })();
9776
9777 /**
9778  * @class Ext.Ajax
9779  * @extends Ext.data.Connection
9780  * <p>The global Ajax request class that provides a simple way to make Ajax requests
9781  * with maximum flexibility.</p>
9782  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9783  * and override them at the request function level only if necessary.</p>
9784  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9785  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9786  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9787  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9788  * </ul></div>
9789  * <pre><code>
9790 // Default headers to pass in every request
9791 Ext.Ajax.defaultHeaders = {
9792     'Powered-By': 'Ext'
9793 };
9794  * </code></pre>
9795  * </p>
9796  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9797  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9798  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9799  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9800  * </ul></div>
9801  * <pre><code>
9802 // Example: show a spinner during all Ajax requests
9803 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9804 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9805 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9806  * </code></pre>
9807  * </p>
9808  * <p>An example request:</p>
9809  * <pre><code>
9810 // Basic request
9811 Ext.Ajax.{@link Ext.data.Connection#request request}({
9812    url: 'foo.php',
9813    success: someFn,
9814    failure: otherFn,
9815    headers: {
9816        'my-header': 'foo'
9817    },
9818    params: { foo: 'bar' }
9819 });
9820
9821 // Simple ajax form submission
9822 Ext.Ajax.{@link Ext.data.Connection#request request}({
9823     form: 'some-form',
9824     params: 'foo=bar'
9825 });
9826  * </code></pre>
9827  * </p>
9828  * @singleton
9829  */
9830 Ext.Ajax = new Ext.data.Connection({
9831     /**
9832      * @cfg {String} url @hide
9833      */
9834     /**
9835      * @cfg {Object} extraParams @hide
9836      */
9837     /**
9838      * @cfg {Object} defaultHeaders @hide
9839      */
9840     /**
9841      * @cfg {String} method (Optional) @hide
9842      */
9843     /**
9844      * @cfg {Number} timeout (Optional) @hide
9845      */
9846     /**
9847      * @cfg {Boolean} autoAbort (Optional) @hide
9848      */
9849
9850     /**
9851      * @cfg {Boolean} disableCaching (Optional) @hide
9852      */
9853
9854     /**
9855      * @property  disableCaching
9856      * True to add a unique cache-buster param to GET requests. (defaults to true)
9857      * @type Boolean
9858      */
9859     /**
9860      * @property  url
9861      * The default URL to be used for requests to the server. (defaults to undefined)
9862      * If the server receives all requests through one URL, setting this once is easier than
9863      * entering it on every request.
9864      * @type String
9865      */
9866     /**
9867      * @property  extraParams
9868      * An object containing properties which are used as extra parameters to each request made
9869      * by this object (defaults to undefined). Session information and other data that you need
9870      * to pass with each request are commonly put here.
9871      * @type Object
9872      */
9873     /**
9874      * @property  defaultHeaders
9875      * An object containing request headers which are added to each request made by this object
9876      * (defaults to undefined).
9877      * @type Object
9878      */
9879     /**
9880      * @property  method
9881      * The default HTTP method to be used for requests. Note that this is case-sensitive and
9882      * should be all caps (defaults to undefined; if not set but params are present will use
9883      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9884      * @type String
9885      */
9886     /**
9887      * @property  timeout
9888      * The timeout in milliseconds to be used for requests. (defaults to 30000)
9889      * @type Number
9890      */
9891
9892     /**
9893      * @property  autoAbort
9894      * Whether a new request should abort any pending requests. (defaults to false)
9895      * @type Boolean
9896      */
9897     autoAbort : false,
9898
9899     /**
9900      * Serialize the passed form into a url encoded string
9901      * @param {String/HTMLElement} form
9902      * @return {String}
9903      */
9904     serializeForm : function(form){
9905         return Ext.lib.Ajax.serializeForm(form);
9906     }
9907 });
9908 /**
9909  * @class Ext.Updater
9910  * @extends Ext.util.Observable
9911  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9912  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9913  * {@link Ext.Element Element} on a specific interval.<br><br>
9914  * Usage:<br>
9915  * <pre><code>
9916  * var el = Ext.get("foo"); // Get Ext.Element object
9917  * var mgr = el.getUpdater();
9918  * mgr.update({
9919         url: "http://myserver.com/index.php",
9920         params: {
9921             param1: "foo",
9922             param2: "bar"
9923         }
9924  * });
9925  * ...
9926  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9927  * <br>
9928  * // or directly (returns the same Updater instance)
9929  * var mgr = new Ext.Updater("myElementId");
9930  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9931  * mgr.on("update", myFcnNeedsToKnow);
9932  * <br>
9933  * // short handed call directly from the element object
9934  * Ext.get("foo").load({
9935         url: "bar.php",
9936         scripts: true,
9937         params: "param1=foo&amp;param2=bar",
9938         text: "Loading Foo..."
9939  * });
9940  * </code></pre>
9941  * @constructor
9942  * Create new Updater directly.
9943  * @param {Mixed} el The element to update
9944  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9945  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9946  */
9947 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
9948 function() {
9949     var BEFOREUPDATE = "beforeupdate",
9950         UPDATE = "update",
9951         FAILURE = "failure";
9952
9953     // private
9954     function processSuccess(response){
9955         var me = this;
9956         me.transaction = null;
9957         if (response.argument.form && response.argument.reset) {
9958             try { // put in try/catch since some older FF releases had problems with this
9959                 response.argument.form.reset();
9960             } catch(e){}
9961         }
9962         if (me.loadScripts) {
9963             me.renderer.render(me.el, response, me,
9964                updateComplete.createDelegate(me, [response]));
9965         } else {
9966             me.renderer.render(me.el, response, me);
9967             updateComplete.call(me, response);
9968         }
9969     }
9970
9971     // private
9972     function updateComplete(response, type, success){
9973         this.fireEvent(type || UPDATE, this.el, response);
9974         if(Ext.isFunction(response.argument.callback)){
9975             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9976         }
9977     }
9978
9979     // private
9980     function processFailure(response){
9981         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9982     }
9983
9984     return {
9985         constructor: function(el, forceNew){
9986             var me = this;
9987             el = Ext.get(el);
9988             if(!forceNew && el.updateManager){
9989                 return el.updateManager;
9990             }
9991             /**
9992              * The Element object
9993              * @type Ext.Element
9994              */
9995             me.el = el;
9996             /**
9997              * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9998              * @type String
9999              */
10000             me.defaultUrl = null;
10001
10002             me.addEvents(
10003                 /**
10004                  * @event beforeupdate
10005                  * Fired before an update is made, return false from your handler and the update is cancelled.
10006                  * @param {Ext.Element} el
10007                  * @param {String/Object/Function} url
10008                  * @param {String/Object} params
10009                  */
10010                 BEFOREUPDATE,
10011                 /**
10012                  * @event update
10013                  * Fired after successful update is made.
10014                  * @param {Ext.Element} el
10015                  * @param {Object} oResponseObject The response Object
10016                  */
10017                 UPDATE,
10018                 /**
10019                  * @event failure
10020                  * Fired on update failure.
10021                  * @param {Ext.Element} el
10022                  * @param {Object} oResponseObject The response Object
10023                  */
10024                 FAILURE
10025             );
10026
10027             Ext.apply(me, Ext.Updater.defaults);
10028             /**
10029              * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10030              * @property sslBlankUrl
10031              * @type String
10032              */
10033             /**
10034              * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10035              * @property disableCaching
10036              * @type Boolean
10037              */
10038             /**
10039              * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10040              * @property indicatorText
10041              * @type String
10042              */
10043             /**
10044              * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10045              * @property showLoadIndicator
10046              * @type String
10047              */
10048             /**
10049              * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10050              * @property timeout
10051              * @type Number
10052              */
10053             /**
10054              * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10055              * @property loadScripts
10056              * @type Boolean
10057              */
10058
10059             /**
10060              * Transaction object of the current executing transaction, or null if there is no active transaction.
10061              */
10062             me.transaction = null;
10063             /**
10064              * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10065              * @type Function
10066              */
10067             me.refreshDelegate = me.refresh.createDelegate(me);
10068             /**
10069              * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10070              * @type Function
10071              */
10072             me.updateDelegate = me.update.createDelegate(me);
10073             /**
10074              * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10075              * @type Function
10076              */
10077             me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10078
10079             /**
10080              * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10081              */
10082             me.renderer = me.renderer || me.getDefaultRenderer();
10083
10084             Ext.Updater.superclass.constructor.call(me);
10085         },
10086
10087         /**
10088          * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10089          * @param {Object} renderer The object implementing the render() method
10090          */
10091         setRenderer : function(renderer){
10092             this.renderer = renderer;
10093         },
10094
10095         /**
10096          * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10097          * @return {Object}
10098          */
10099         getRenderer : function(){
10100            return this.renderer;
10101         },
10102
10103         /**
10104          * This is an overrideable method which returns a reference to a default
10105          * renderer class if none is specified when creating the Ext.Updater.
10106          * Defaults to {@link Ext.Updater.BasicRenderer}
10107          */
10108         getDefaultRenderer: function() {
10109             return new Ext.Updater.BasicRenderer();
10110         },
10111
10112         /**
10113          * Sets the default URL used for updates.
10114          * @param {String/Function} defaultUrl The url or a function to call to get the url
10115          */
10116         setDefaultUrl : function(defaultUrl){
10117             this.defaultUrl = defaultUrl;
10118         },
10119
10120         /**
10121          * Get the Element this Updater is bound to
10122          * @return {Ext.Element} The element
10123          */
10124         getEl : function(){
10125             return this.el;
10126         },
10127
10128         /**
10129          * Performs an <b>asynchronous</b> request, updating this element with the response.
10130          * If params are specified it uses POST, otherwise it uses GET.<br><br>
10131          * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10132          * will not have been fully updated when the function returns. To post-process the returned
10133          * data, use the callback option, or an <b><code>update</code></b> event handler.
10134          * @param {Object} options A config object containing any of the following options:<ul>
10135          * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10136          * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10137          * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10138          * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10139          * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10140          * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10141          * string, or as an object containing properties which represent parameters,
10142          * or as a function, which returns such an object.</p></li>
10143          * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10144          * any &lt;script&gt; tags embedded in the response text will be extracted
10145          * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10146          * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10147          * <li>callback : <b>Function</b><p class="sub-desc">A function to
10148          * be called when the response from the server arrives. The following
10149          * parameters are passed:<ul>
10150          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10151          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10152          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10153          * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10154          * </p></li>
10155          * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10156          * to execute the callback (The callback's <code>this</code> reference.) If the
10157          * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10158          * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10159          * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10160          * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10161          * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10162          * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10163          * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10164          * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
10165          * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10166          * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10167          * requests, this option causes an extra, auto-generated parameter to be appended to the request
10168          * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10169          * <p>
10170          * For example:
10171     <pre><code>
10172     um.update({
10173         url: "your-url.php",
10174         params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10175         callback: yourFunction,
10176         scope: yourObject, //(optional scope)
10177         discardUrl: true,
10178         nocache: true,
10179         text: "Loading...",
10180         timeout: 60,
10181         scripts: false // Save time by avoiding RegExp execution.
10182     });
10183     </code></pre>
10184          */
10185         update : function(url, params, callback, discardUrl){
10186             var me = this,
10187                 cfg,
10188                 callerScope;
10189
10190             if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10191                 if(Ext.isObject(url)){ // must be config object
10192                     cfg = url;
10193                     url = cfg.url;
10194                     params = params || cfg.params;
10195                     callback = callback || cfg.callback;
10196                     discardUrl = discardUrl || cfg.discardUrl;
10197                     callerScope = cfg.scope;
10198                     if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10199                     if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10200                     if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10201                     if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10202                 }
10203                 me.showLoading();
10204
10205                 if(!discardUrl){
10206                     me.defaultUrl = url;
10207                 }
10208                 if(Ext.isFunction(url)){
10209                     url = url.call(me);
10210                 }
10211
10212                 var o = Ext.apply({}, {
10213                     url : url,
10214                     params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10215                     success: processSuccess,
10216                     failure: processFailure,
10217                     scope: me,
10218                     callback: undefined,
10219                     timeout: (me.timeout*1000),
10220                     disableCaching: me.disableCaching,
10221                     argument: {
10222                         "options": cfg,
10223                         "url": url,
10224                         "form": null,
10225                         "callback": callback,
10226                         "scope": callerScope || window,
10227                         "params": params
10228                     }
10229                 }, cfg);
10230
10231                 me.transaction = Ext.Ajax.request(o);
10232             }
10233         },
10234
10235         /**
10236          * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10237          * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10238          * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10239          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10240          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10241          * DOM <code>&lt;form></code> element temporarily modified to have its
10242          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10243          * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
10244          * but removed after the return data has been gathered.</p>
10245          * <p>Be aware that file upload packets, sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>
10246          * and some server technologies (notably JEE) may require some custom processing in order to
10247          * retrieve parameter names and parameter values from the packet content.</p>
10248          * @param {String/HTMLElement} form The form Id or form element
10249          * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10250          * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10251          * @param {Function} callback (optional) Callback when transaction is complete. The following
10252          * parameters are passed:<ul>
10253          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10254          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10255          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10256          */
10257         formUpdate : function(form, url, reset, callback){
10258             var me = this;
10259             if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10260                 if(Ext.isFunction(url)){
10261                     url = url.call(me);
10262                 }
10263                 form = Ext.getDom(form);
10264                 me.transaction = Ext.Ajax.request({
10265                     form: form,
10266                     url:url,
10267                     success: processSuccess,
10268                     failure: processFailure,
10269                     scope: me,
10270                     timeout: (me.timeout*1000),
10271                     argument: {
10272                         "url": url,
10273                         "form": form,
10274                         "callback": callback,
10275                         "reset": reset
10276                     }
10277                 });
10278                 me.showLoading.defer(1, me);
10279             }
10280         },
10281
10282         /**
10283          * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
10284          * @param {Number} interval How often to update (in seconds).
10285          * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10286          * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
10287          * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10288          * sepcified as part of a config object passed as this paramter if needed.
10289          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10290          * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
10291          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10292          * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10293          */
10294         startAutoRefresh : function(interval, url, params, callback, refreshNow){
10295             var me = this;
10296             if(refreshNow){
10297                 me.update(url || me.defaultUrl, params, callback, true);
10298             }
10299             if(me.autoRefreshProcId){
10300                 clearInterval(me.autoRefreshProcId);
10301             }
10302             me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10303         },
10304
10305         /**
10306          * Stop auto refresh on this element.
10307          */
10308         stopAutoRefresh : function(){
10309             if(this.autoRefreshProcId){
10310                 clearInterval(this.autoRefreshProcId);
10311                 delete this.autoRefreshProcId;
10312             }
10313         },
10314
10315         /**
10316          * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10317          */
10318         isAutoRefreshing : function(){
10319            return !!this.autoRefreshProcId;
10320         },
10321
10322         /**
10323          * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10324          * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10325          */
10326         showLoading : function(){
10327             if(this.showLoadIndicator){
10328                 this.el.dom.innerHTML = this.indicatorText;
10329             }
10330         },
10331
10332         /**
10333          * Aborts the currently executing transaction, if any.
10334          */
10335         abort : function(){
10336             if(this.transaction){
10337                 Ext.Ajax.abort(this.transaction);
10338             }
10339         },
10340
10341         /**
10342          * Returns true if an update is in progress, otherwise false.
10343          * @return {Boolean}
10344          */
10345         isUpdating : function(){
10346             return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10347         },
10348
10349         /**
10350          * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10351          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10352          */
10353         refresh : function(callback){
10354             if(this.defaultUrl){
10355                 this.update(this.defaultUrl, null, callback, true);
10356             }
10357         }
10358     }
10359 }());
10360
10361 /**
10362  * @class Ext.Updater.defaults
10363  * The defaults collection enables customizing the default properties of Updater
10364  */
10365 Ext.Updater.defaults = {
10366    /**
10367      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10368      * @type Number
10369      */
10370     timeout : 30,
10371     /**
10372      * True to append a unique parameter to GET requests to disable caching (defaults to false).
10373      * @type Boolean
10374      */
10375     disableCaching : false,
10376     /**
10377      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10378      * @type Boolean
10379      */
10380     showLoadIndicator : true,
10381     /**
10382      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
10383      * @type String
10384      */
10385     indicatorText : '<div class="loading-indicator">Loading...</div>',
10386      /**
10387      * True to process scripts by default (defaults to false).
10388      * @type Boolean
10389      */
10390     loadScripts : false,
10391     /**
10392     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10393     * @type String
10394     */
10395     sslBlankUrl : Ext.SSL_SECURE_URL
10396 };
10397
10398
10399 /**
10400  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10401  * Usage:
10402  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10403  * @param {Mixed} el The element to update
10404  * @param {String} url The url
10405  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10406  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10407  * example: {disableCaching:true, indicatorText: "Loading data..."}
10408  * @static
10409  * @deprecated
10410  * @member Ext.Updater
10411  */
10412 Ext.Updater.updateElement = function(el, url, params, options){
10413     var um = Ext.get(el).getUpdater();
10414     Ext.apply(um, options);
10415     um.update(url, params, options ? options.callback : null);
10416 };
10417
10418 /**
10419  * @class Ext.Updater.BasicRenderer
10420  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10421  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10422  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10423  */
10424 Ext.Updater.BasicRenderer = function(){};
10425
10426 Ext.Updater.BasicRenderer.prototype = {
10427     /**
10428      * This method is called when an Ajax response is received, and an Element needs updating.
10429      * @param {Ext.Element} el The element being rendered
10430      * @param {Object} xhr The XMLHttpRequest object
10431      * @param {Updater} updateManager The calling update manager
10432      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10433      */
10434      render : function(el, response, updateManager, callback){
10435         el.update(response.responseText, updateManager.loadScripts, callback);
10436     }
10437 };/**
10438  * @class Date
10439  *
10440  * The date parsing and formatting syntax contains a subset of
10441  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10442  * supported will provide results equivalent to their PHP versions.
10443  *
10444  * The following is a list of all currently supported formats:
10445  * <pre>
10446 Format  Description                                                               Example returned values
10447 ------  -----------------------------------------------------------------------   -----------------------
10448   d     Day of the month, 2 digits with leading zeros                             01 to 31
10449   D     A short textual representation of the day of the week                     Mon to Sun
10450   j     Day of the month without leading zeros                                    1 to 31
10451   l     A full textual representation of the day of the week                      Sunday to Saturday
10452   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
10453   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
10454   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
10455   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
10456   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
10457   F     A full textual representation of a month, such as January or March        January to December
10458   m     Numeric representation of a month, with leading zeros                     01 to 12
10459   M     A short textual representation of a month                                 Jan to Dec
10460   n     Numeric representation of a month, without leading zeros                  1 to 12
10461   t     Number of days in the given month                                         28 to 31
10462   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
10463   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
10464         belongs to the previous or next year, that year is used instead)
10465   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
10466   y     A two digit representation of a year                                      Examples: 99 or 03
10467   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
10468   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
10469   g     12-hour format of an hour without leading zeros                           1 to 12
10470   G     24-hour format of an hour without leading zeros                           0 to 23
10471   h     12-hour format of an hour with leading zeros                              01 to 12
10472   H     24-hour format of an hour with leading zeros                              00 to 23
10473   i     Minutes, with leading zeros                                               00 to 59
10474   s     Seconds, with leading zeros                                               00 to 59
10475   u     Decimal fraction of a second                                              Examples:
10476         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
10477                                                                                   100 (i.e. 0.100s) or
10478                                                                                   999 (i.e. 0.999s) or
10479                                                                                   999876543210 (i.e. 0.999876543210s)
10480   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
10481   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
10482   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
10483   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
10484   c     ISO 8601 date
10485         Notes:                                                                    Examples:
10486         1) If unspecified, the month / day defaults to the current month / day,   1991 or
10487            the time defaults to midnight, while the timezone defaults to the      1992-10 or
10488            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10489            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
10490            are optional.                                                          1995-07-18T17:21:28-02:00 or
10491         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
10492            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
10493            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
10494         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
10495         date-time granularity which are supported, or see                         2000-02-13T21:25:33
10496         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
10497   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
10498   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10499                                                                                   \/Date(1238606590509+0800)\/
10500 </pre>
10501  *
10502  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10503  * <pre><code>
10504 // Sample date:
10505 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10506
10507 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10508 document.write(dt.format('Y-m-d'));                           // 2007-01-10
10509 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
10510 document.write(dt.format('l, \\t\\he jS \\of F Y h:i:s A'));  // Wednesday, the 10th of January 2007 03:05:01 PM
10511 </code></pre>
10512  *
10513  * Here are some standard date/time patterns that you might find helpful.  They
10514  * are not part of the source of Date.js, but to use them you can simply copy this
10515  * block of code into any script that is included after Date.js and they will also become
10516  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
10517  * <pre><code>
10518 Date.patterns = {
10519     ISO8601Long:"Y-m-d H:i:s",
10520     ISO8601Short:"Y-m-d",
10521     ShortDate: "n/j/Y",
10522     LongDate: "l, F d, Y",
10523     FullDateTime: "l, F d, Y g:i:s A",
10524     MonthDay: "F d",
10525     ShortTime: "g:i A",
10526     LongTime: "g:i:s A",
10527     SortableDateTime: "Y-m-d\\TH:i:s",
10528     UniversalSortableDateTime: "Y-m-d H:i:sO",
10529     YearMonth: "F, Y"
10530 };
10531 </code></pre>
10532  *
10533  * Example usage:
10534  * <pre><code>
10535 var dt = new Date();
10536 document.write(dt.format(Date.patterns.ShortDate));
10537 </code></pre>
10538  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10539  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10540  */
10541
10542 /*
10543  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10544  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10545  * They generate precompiled functions from format patterns instead of parsing and
10546  * processing each pattern every time a date is formatted. These functions are available
10547  * on every Date object.
10548  */
10549
10550 (function() {
10551
10552 /**
10553  * Global flag which determines if strict date parsing should be used.
10554  * Strict date parsing will not roll-over invalid dates, which is the
10555  * default behaviour of javascript Date objects.
10556  * (see {@link #parseDate} for more information)
10557  * Defaults to <tt>false</tt>.
10558  * @static
10559  * @type Boolean
10560 */
10561 Date.useStrict = false;
10562
10563
10564 // create private copy of Ext's String.format() method
10565 // - to remove unnecessary dependency
10566 // - to resolve namespace conflict with M$-Ajax's implementation
10567 function xf(format) {
10568     var args = Array.prototype.slice.call(arguments, 1);
10569     return format.replace(/\{(\d+)\}/g, function(m, i) {
10570         return args[i];
10571     });
10572 }
10573
10574
10575 // private
10576 Date.formatCodeToRegex = function(character, currentGroup) {
10577     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10578     var p = Date.parseCodes[character];
10579
10580     if (p) {
10581       p = typeof p == 'function'? p() : p;
10582       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10583     }
10584
10585     return p ? Ext.applyIf({
10586       c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10587     }, p) : {
10588         g:0,
10589         c:null,
10590         s:Ext.escapeRe(character) // treat unrecognised characters as literals
10591     }
10592 };
10593
10594 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10595 var $f = Date.formatCodeToRegex;
10596
10597 Ext.apply(Date, {
10598     /**
10599      * <p>An object hash in which each property is a date parsing function. The property name is the
10600      * format string which that function parses.</p>
10601      * <p>This object is automatically populated with date parsing functions as
10602      * date formats are requested for Ext standard formatting strings.</p>
10603      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10604      * may be used as a format string to {@link #parseDate}.<p>
10605      * <p>Example:</p><pre><code>
10606 Date.parseFunctions['x-date-format'] = myDateParser;
10607 </code></pre>
10608      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10609      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10610      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10611      * (i.e. prevent javascript Date "rollover") (The default must be false).
10612      * Invalid date strings should return null when parsed.</div></li>
10613      * </ul></div></p>
10614      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10615      * formatting function must be placed into the {@link #formatFunctions} property.
10616      * @property parseFunctions
10617      * @static
10618      * @type Object
10619      */
10620     parseFunctions: {
10621         "M$": function(input, strict) {
10622             // note: the timezone offset is ignored since the M$ Ajax server sends
10623             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10624             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10625             var r = (input || '').match(re);
10626             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10627         }
10628     },
10629     parseRegexes: [],
10630
10631     /**
10632      * <p>An object hash in which each property is a date formatting function. The property name is the
10633      * format string which corresponds to the produced formatted date string.</p>
10634      * <p>This object is automatically populated with date formatting functions as
10635      * date formats are requested for Ext standard formatting strings.</p>
10636      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10637      * may be used as a format string to {@link #format}. Example:</p><pre><code>
10638 Date.formatFunctions['x-date-format'] = myDateFormatter;
10639 </code></pre>
10640      * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10641      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10642      * </ul></div></p>
10643      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10644      * parsing function must be placed into the {@link #parseFunctions} property.
10645      * @property formatFunctions
10646      * @static
10647      * @type Object
10648      */
10649     formatFunctions: {
10650         "M$": function() {
10651             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10652             return '\\/Date(' + this.getTime() + ')\\/';
10653         }
10654     },
10655
10656     y2kYear : 50,
10657
10658     /**
10659      * Date interval constant
10660      * @static
10661      * @type String
10662      */
10663     MILLI : "ms",
10664
10665     /**
10666      * Date interval constant
10667      * @static
10668      * @type String
10669      */
10670     SECOND : "s",
10671
10672     /**
10673      * Date interval constant
10674      * @static
10675      * @type String
10676      */
10677     MINUTE : "mi",
10678
10679     /** Date interval constant
10680      * @static
10681      * @type String
10682      */
10683     HOUR : "h",
10684
10685     /**
10686      * Date interval constant
10687      * @static
10688      * @type String
10689      */
10690     DAY : "d",
10691
10692     /**
10693      * Date interval constant
10694      * @static
10695      * @type String
10696      */
10697     MONTH : "mo",
10698
10699     /**
10700      * Date interval constant
10701      * @static
10702      * @type String
10703      */
10704     YEAR : "y",
10705
10706     /**
10707      * <p>An object hash containing default date values used during date parsing.</p>
10708      * <p>The following properties are available:<div class="mdetail-params"><ul>
10709      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10710      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10711      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10712      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10713      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10714      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10715      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10716      * </ul></div></p>
10717      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10718      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10719      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10720      * It is the responsiblity of the developer to account for this.</b></p>
10721      * Example Usage:
10722      * <pre><code>
10723 // set default day value to the first day of the month
10724 Date.defaults.d = 1;
10725
10726 // parse a February date string containing only year and month values.
10727 // setting the default day value to 1 prevents weird date rollover issues
10728 // when attempting to parse the following date string on, for example, March 31st 2009.
10729 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10730 </code></pre>
10731      * @property defaults
10732      * @static
10733      * @type Object
10734      */
10735     defaults: {},
10736
10737     /**
10738      * An array of textual day names.
10739      * Override these values for international dates.
10740      * Example:
10741      * <pre><code>
10742 Date.dayNames = [
10743     'SundayInYourLang',
10744     'MondayInYourLang',
10745     ...
10746 ];
10747 </code></pre>
10748      * @type Array
10749      * @static
10750      */
10751     dayNames : [
10752         "Sunday",
10753         "Monday",
10754         "Tuesday",
10755         "Wednesday",
10756         "Thursday",
10757         "Friday",
10758         "Saturday"
10759     ],
10760
10761     /**
10762      * An array of textual month names.
10763      * Override these values for international dates.
10764      * Example:
10765      * <pre><code>
10766 Date.monthNames = [
10767     'JanInYourLang',
10768     'FebInYourLang',
10769     ...
10770 ];
10771 </code></pre>
10772      * @type Array
10773      * @static
10774      */
10775     monthNames : [
10776         "January",
10777         "February",
10778         "March",
10779         "April",
10780         "May",
10781         "June",
10782         "July",
10783         "August",
10784         "September",
10785         "October",
10786         "November",
10787         "December"
10788     ],
10789
10790     /**
10791      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10792      * Override these values for international dates.
10793      * Example:
10794      * <pre><code>
10795 Date.monthNumbers = {
10796     'ShortJanNameInYourLang':0,
10797     'ShortFebNameInYourLang':1,
10798     ...
10799 };
10800 </code></pre>
10801      * @type Object
10802      * @static
10803      */
10804     monthNumbers : {
10805         Jan:0,
10806         Feb:1,
10807         Mar:2,
10808         Apr:3,
10809         May:4,
10810         Jun:5,
10811         Jul:6,
10812         Aug:7,
10813         Sep:8,
10814         Oct:9,
10815         Nov:10,
10816         Dec:11
10817     },
10818
10819     /**
10820      * Get the short month name for the given month number.
10821      * Override this function for international dates.
10822      * @param {Number} month A zero-based javascript month number.
10823      * @return {String} The short month name.
10824      * @static
10825      */
10826     getShortMonthName : function(month) {
10827         return Date.monthNames[month].substring(0, 3);
10828     },
10829
10830     /**
10831      * Get the short day name for the given day number.
10832      * Override this function for international dates.
10833      * @param {Number} day A zero-based javascript day number.
10834      * @return {String} The short day name.
10835      * @static
10836      */
10837     getShortDayName : function(day) {
10838         return Date.dayNames[day].substring(0, 3);
10839     },
10840
10841     /**
10842      * Get the zero-based javascript month number for the given short/full month name.
10843      * Override this function for international dates.
10844      * @param {String} name The short/full month name.
10845      * @return {Number} The zero-based javascript month number.
10846      * @static
10847      */
10848     getMonthNumber : function(name) {
10849         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10850         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10851     },
10852
10853     /**
10854      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10855      * Formatting functions are strings (or functions which return strings) which
10856      * will return the appropriate value when evaluated in the context of the Date object
10857      * from which the {@link #format} method is called.
10858      * Add to / override these mappings for custom date formatting.
10859      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10860      * Example:
10861      * <pre><code>
10862 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10863 (new Date()).format("X"); // returns the current day of the month
10864 </code></pre>
10865      * @type Object
10866      * @static
10867      */
10868     formatCodes : {
10869         d: "String.leftPad(this.getDate(), 2, '0')",
10870         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10871         j: "this.getDate()",
10872         l: "Date.dayNames[this.getDay()]",
10873         N: "(this.getDay() ? this.getDay() : 7)",
10874         S: "this.getSuffix()",
10875         w: "this.getDay()",
10876         z: "this.getDayOfYear()",
10877         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10878         F: "Date.monthNames[this.getMonth()]",
10879         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10880         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10881         n: "(this.getMonth() + 1)",
10882         t: "this.getDaysInMonth()",
10883         L: "(this.isLeapYear() ? 1 : 0)",
10884         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10885         Y: "this.getFullYear()",
10886         y: "('' + this.getFullYear()).substring(2, 4)",
10887         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10888         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10889         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10890         G: "this.getHours()",
10891         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10892         H: "String.leftPad(this.getHours(), 2, '0')",
10893         i: "String.leftPad(this.getMinutes(), 2, '0')",
10894         s: "String.leftPad(this.getSeconds(), 2, '0')",
10895         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10896         O: "this.getGMTOffset()",
10897         P: "this.getGMTOffset(true)",
10898         T: "this.getTimezone()",
10899         Z: "(this.getTimezoneOffset() * -60)",
10900
10901         c: function() { // ISO-8601 -- GMT format
10902             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10903                 var e = c.charAt(i);
10904                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10905             }
10906             return code.join(" + ");
10907         },
10908         /*
10909         c: function() { // ISO-8601 -- UTC format
10910             return [
10911               "this.getUTCFullYear()", "'-'",
10912               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10913               "String.leftPad(this.getUTCDate(), 2, '0')",
10914               "'T'",
10915               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10916               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10917               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10918               "'Z'"
10919             ].join(" + ");
10920         },
10921         */
10922
10923         U: "Math.round(this.getTime() / 1000)"
10924     },
10925
10926     /**
10927      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10928      * @param {Number} year 4-digit year
10929      * @param {Number} month 1-based month-of-year
10930      * @param {Number} day Day of month
10931      * @param {Number} hour (optional) Hour
10932      * @param {Number} minute (optional) Minute
10933      * @param {Number} second (optional) Second
10934      * @param {Number} millisecond (optional) Millisecond
10935      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10936      * @static
10937      */
10938     isValid : function(y, m, d, h, i, s, ms) {
10939         // setup defaults
10940         h = h || 0;
10941         i = i || 0;
10942         s = s || 0;
10943         ms = ms || 0;
10944
10945         var dt = new Date(y, m - 1, d, h, i, s, ms);
10946
10947         return y == dt.getFullYear() &&
10948             m == dt.getMonth() + 1 &&
10949             d == dt.getDate() &&
10950             h == dt.getHours() &&
10951             i == dt.getMinutes() &&
10952             s == dt.getSeconds() &&
10953             ms == dt.getMilliseconds();
10954     },
10955
10956     /**
10957      * Parses the passed string using the specified date format.
10958      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10959      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10960      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10961      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10962      * Keep in mind that the input date string must precisely match the specified format string
10963      * in order for the parse operation to be successful (failed parse operations return a null value).
10964      * <p>Example:</p><pre><code>
10965 //dt = Fri May 25 2007 (current date)
10966 var dt = new Date();
10967
10968 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
10969 dt = Date.parseDate("2006", "Y");
10970
10971 //dt = Sun Jan 15 2006 (all date parts specified)
10972 dt = Date.parseDate("2006-01-15", "Y-m-d");
10973
10974 //dt = Sun Jan 15 2006 15:20:01
10975 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10976
10977 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10978 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10979 </code></pre>
10980      * @param {String} input The raw date string.
10981      * @param {String} format The expected date string format.
10982      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10983                         (defaults to false). Invalid date strings will return null when parsed.
10984      * @return {Date} The parsed Date.
10985      * @static
10986      */
10987     parseDate : function(input, format, strict) {
10988         var p = Date.parseFunctions;
10989         if (p[format] == null) {
10990             Date.createParser(format);
10991         }
10992         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10993     },
10994
10995     // private
10996     getFormatCode : function(character) {
10997         var f = Date.formatCodes[character];
10998
10999         if (f) {
11000           f = typeof f == 'function'? f() : f;
11001           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11002         }
11003
11004         // note: unknown characters are treated as literals
11005         return f || ("'" + String.escape(character) + "'");
11006     },
11007
11008     // private
11009     createFormat : function(format) {
11010         var code = [],
11011             special = false,
11012             ch = '';
11013
11014         for (var i = 0; i < format.length; ++i) {
11015             ch = format.charAt(i);
11016             if (!special && ch == "\\") {
11017                 special = true;
11018             } else if (special) {
11019                 special = false;
11020                 code.push("'" + String.escape(ch) + "'");
11021             } else {
11022                 code.push(Date.getFormatCode(ch))
11023             }
11024         }
11025         Date.formatFunctions[format] = new Function("return " + code.join('+'));
11026     },
11027
11028     // private
11029     createParser : function() {
11030         var code = [
11031             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11032                 "def = Date.defaults,",
11033                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11034
11035             "if(results){",
11036                 "{1}",
11037
11038                 "if(u != null){", // i.e. unix time is defined
11039                     "v = new Date(u * 1000);", // give top priority to UNIX time
11040                 "}else{",
11041                     // create Date object representing midnight of the current day;
11042                     // this will provide us with our date defaults
11043                     // (note: clearTime() handles Daylight Saving Time automatically)
11044                     "dt = (new Date()).clearTime();",
11045
11046                     // date calculations (note: these calculations create a dependency on Ext.num())
11047                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11048                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11049                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11050
11051                     // time calculations (note: these calculations create a dependency on Ext.num())
11052                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11053                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11054                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11055                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11056
11057                     "if(z >= 0 && y >= 0){",
11058                         // both the year and zero-based day of year are defined and >= 0.
11059                         // these 2 values alone provide sufficient info to create a full date object
11060
11061                         // create Date object representing January 1st for the given year
11062                         "v = new Date(y, 0, 1, h, i, s, ms);",
11063
11064                         // then add day of year, checking for Date "rollover" if necessary
11065                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11066                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11067                         "v = null;", // invalid date, so return null
11068                     "}else{",
11069                         // plain old Date object
11070                         "v = new Date(y, m, d, h, i, s, ms);",
11071                     "}",
11072                 "}",
11073             "}",
11074
11075             "if(v){",
11076                 // favour UTC offset over GMT offset
11077                 "if(zz != null){",
11078                     // reset to UTC, then add offset
11079                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11080                 "}else if(o){",
11081                     // reset to GMT, then add offset
11082                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11083                 "}",
11084             "}",
11085
11086             "return v;"
11087         ].join('\n');
11088
11089         return function(format) {
11090             var regexNum = Date.parseRegexes.length,
11091                 currentGroup = 1,
11092                 calc = [],
11093                 regex = [],
11094                 special = false,
11095                 ch = "";
11096
11097             for (var i = 0; i < format.length; ++i) {
11098                 ch = format.charAt(i);
11099                 if (!special && ch == "\\") {
11100                     special = true;
11101                 } else if (special) {
11102                     special = false;
11103                     regex.push(String.escape(ch));
11104                 } else {
11105                     var obj = $f(ch, currentGroup);
11106                     currentGroup += obj.g;
11107                     regex.push(obj.s);
11108                     if (obj.g && obj.c) {
11109                         calc.push(obj.c);
11110                     }
11111                 }
11112             }
11113
11114             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11115             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11116         }
11117     }(),
11118
11119     // private
11120     parseCodes : {
11121         /*
11122          * Notes:
11123          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11124          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11125          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11126          */
11127         d: {
11128             g:1,
11129             c:"d = parseInt(results[{0}], 10);\n",
11130             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11131         },
11132         j: {
11133             g:1,
11134             c:"d = parseInt(results[{0}], 10);\n",
11135             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11136         },
11137         D: function() {
11138             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11139             return {
11140                 g:0,
11141                 c:null,
11142                 s:"(?:" + a.join("|") +")"
11143             }
11144         },
11145         l: function() {
11146             return {
11147                 g:0,
11148                 c:null,
11149                 s:"(?:" + Date.dayNames.join("|") + ")"
11150             }
11151         },
11152         N: {
11153             g:0,
11154             c:null,
11155             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11156         },
11157         S: {
11158             g:0,
11159             c:null,
11160             s:"(?:st|nd|rd|th)"
11161         },
11162         w: {
11163             g:0,
11164             c:null,
11165             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11166         },
11167         z: {
11168             g:1,
11169             c:"z = parseInt(results[{0}], 10);\n",
11170             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11171         },
11172         W: {
11173             g:0,
11174             c:null,
11175             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11176         },
11177         F: function() {
11178             return {
11179                 g:1,
11180                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11181                 s:"(" + Date.monthNames.join("|") + ")"
11182             }
11183         },
11184         M: function() {
11185             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11186             return Ext.applyIf({
11187                 s:"(" + a.join("|") + ")"
11188             }, $f("F"));
11189         },
11190         m: {
11191             g:1,
11192             c:"m = parseInt(results[{0}], 10) - 1;\n",
11193             s:"(\\d{2})" // month number with leading zeros (01 - 12)
11194         },
11195         n: {
11196             g:1,
11197             c:"m = parseInt(results[{0}], 10) - 1;\n",
11198             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11199         },
11200         t: {
11201             g:0,
11202             c:null,
11203             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11204         },
11205         L: {
11206             g:0,
11207             c:null,
11208             s:"(?:1|0)"
11209         },
11210         o: function() {
11211             return $f("Y");
11212         },
11213         Y: {
11214             g:1,
11215             c:"y = parseInt(results[{0}], 10);\n",
11216             s:"(\\d{4})" // 4-digit year
11217         },
11218         y: {
11219             g:1,
11220             c:"var ty = parseInt(results[{0}], 10);\n"
11221                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11222             s:"(\\d{1,2})"
11223         },
11224         a: {
11225             g:1,
11226             c:"if (results[{0}] == 'am') {\n"
11227                 + "if (!h || h == 12) { h = 0; }\n"
11228                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11229             s:"(am|pm)"
11230         },
11231         A: {
11232             g:1,
11233             c:"if (results[{0}] == 'AM') {\n"
11234                 + "if (!h || h == 12) { h = 0; }\n"
11235                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11236             s:"(AM|PM)"
11237         },
11238         g: function() {
11239             return $f("G");
11240         },
11241         G: {
11242             g:1,
11243             c:"h = parseInt(results[{0}], 10);\n",
11244             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11245         },
11246         h: function() {
11247             return $f("H");
11248         },
11249         H: {
11250             g:1,
11251             c:"h = parseInt(results[{0}], 10);\n",
11252             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
11253         },
11254         i: {
11255             g:1,
11256             c:"i = parseInt(results[{0}], 10);\n",
11257             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11258         },
11259         s: {
11260             g:1,
11261             c:"s = parseInt(results[{0}], 10);\n",
11262             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11263         },
11264         u: {
11265             g:1,
11266             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11267             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11268         },
11269         O: {
11270             g:1,
11271             c:[
11272                 "o = results[{0}];",
11273                 "var sn = o.substring(0,1),", // get + / - sign
11274                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11275                     "mn = o.substring(3,5) % 60;", // get minutes
11276                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
11277             ].join("\n"),
11278             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11279         },
11280         P: {
11281             g:1,
11282             c:[
11283                 "o = results[{0}];",
11284                 "var sn = o.substring(0,1),", // get + / - sign
11285                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11286                     "mn = o.substring(4,6) % 60;", // get minutes
11287                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
11288             ].join("\n"),
11289             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11290         },
11291         T: {
11292             g:0,
11293             c:null,
11294             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11295         },
11296         Z: {
11297             g:1,
11298             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11299                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11300             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11301         },
11302         c: function() {
11303             var calc = [],
11304                 arr = [
11305                     $f("Y", 1), // year
11306                     $f("m", 2), // month
11307                     $f("d", 3), // day
11308                     $f("h", 4), // hour
11309                     $f("i", 5), // minute
11310                     $f("s", 6), // second
11311                     {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11312                     {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
11313                         "if(results[8]) {", // timezone specified
11314                             "if(results[8] == 'Z'){",
11315                                 "zz = 0;", // UTC
11316                             "}else if (results[8].indexOf(':') > -1){",
11317                                 $f("P", 8).c, // timezone offset with colon separator
11318                             "}else{",
11319                                 $f("O", 8).c, // timezone offset without colon separator
11320                             "}",
11321                         "}"
11322                     ].join('\n')}
11323                 ];
11324
11325             for (var i = 0, l = arr.length; i < l; ++i) {
11326                 calc.push(arr[i].c);
11327             }
11328
11329             return {
11330                 g:1,
11331                 c:calc.join(""),
11332                 s:[
11333                     arr[0].s, // year (required)
11334                     "(?:", "-", arr[1].s, // month (optional)
11335                         "(?:", "-", arr[2].s, // day (optional)
11336                             "(?:",
11337                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11338                                 arr[3].s, ":", arr[4].s,  // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
11339                                 "(?::", arr[5].s, ")?", // seconds (optional)
11340                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11341                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11342                             ")?",
11343                         ")?",
11344                     ")?"
11345                 ].join("")
11346             }
11347         },
11348         U: {
11349             g:1,
11350             c:"u = parseInt(results[{0}], 10);\n",
11351             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11352         }
11353     }
11354 });
11355
11356 }());
11357
11358 Ext.apply(Date.prototype, {
11359     // private
11360     dateFormat : function(format) {
11361         if (Date.formatFunctions[format] == null) {
11362             Date.createFormat(format);
11363         }
11364         return Date.formatFunctions[format].call(this);
11365     },
11366
11367     /**
11368      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11369      *
11370      * Note: The date string returned by the javascript Date object's toString() method varies
11371      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11372      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11373      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11374      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11375      * from the GMT offset portion of the date string.
11376      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11377      */
11378     getTimezone : function() {
11379         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11380         //
11381         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11382         // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
11383         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11384         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11385         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11386         //
11387         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11388         // step 1: (?:\((.*)\) -- find timezone in parentheses
11389         // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
11390         // step 3: remove all non uppercase characters found in step 1 and 2
11391         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11392     },
11393
11394     /**
11395      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11396      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11397      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11398      */
11399     getGMTOffset : function(colon) {
11400         return (this.getTimezoneOffset() > 0 ? "-" : "+")
11401             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11402             + (colon ? ":" : "")
11403             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11404     },
11405
11406     /**
11407      * Get the numeric day number of the year, adjusted for leap year.
11408      * @return {Number} 0 to 364 (365 in leap years).
11409      */
11410     getDayOfYear: function() {
11411         var num = 0,
11412             d = this.clone(),
11413             m = this.getMonth(),
11414             i;
11415
11416         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11417             num += d.getDaysInMonth();
11418         }
11419         return num + this.getDate() - 1;
11420     },
11421
11422     /**
11423      * Get the numeric ISO-8601 week number of the year.
11424      * (equivalent to the format specifier 'W', but without a leading zero).
11425      * @return {Number} 1 to 53
11426      */
11427     getWeekOfYear : function() {
11428         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11429         var ms1d = 864e5, // milliseconds in a day
11430             ms7d = 7 * ms1d; // milliseconds in a week
11431
11432         return function() { // return a closure so constants get calculated only once
11433             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11434                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11435                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11436
11437             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11438         }
11439     }(),
11440
11441     /**
11442      * Checks if the current date falls within a leap year.
11443      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11444      */
11445     isLeapYear : function() {
11446         var year = this.getFullYear();
11447         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11448     },
11449
11450     /**
11451      * Get the first day of the current month, adjusted for leap year.  The returned value
11452      * is the numeric day index within the week (0-6) which can be used in conjunction with
11453      * the {@link #monthNames} array to retrieve the textual day name.
11454      * Example:
11455      * <pre><code>
11456 var dt = new Date('1/10/2007');
11457 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11458 </code></pre>
11459      * @return {Number} The day number (0-6).
11460      */
11461     getFirstDayOfMonth : function() {
11462         var day = (this.getDay() - (this.getDate() - 1)) % 7;
11463         return (day < 0) ? (day + 7) : day;
11464     },
11465
11466     /**
11467      * Get the last day of the current month, adjusted for leap year.  The returned value
11468      * is the numeric day index within the week (0-6) which can be used in conjunction with
11469      * the {@link #monthNames} array to retrieve the textual day name.
11470      * Example:
11471      * <pre><code>
11472 var dt = new Date('1/10/2007');
11473 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11474 </code></pre>
11475      * @return {Number} The day number (0-6).
11476      */
11477     getLastDayOfMonth : function() {
11478         return this.getLastDateOfMonth().getDay();
11479     },
11480
11481
11482     /**
11483      * Get the date of the first day of the month in which this date resides.
11484      * @return {Date}
11485      */
11486     getFirstDateOfMonth : function() {
11487         return new Date(this.getFullYear(), this.getMonth(), 1);
11488     },
11489
11490     /**
11491      * Get the date of the last day of the month in which this date resides.
11492      * @return {Date}
11493      */
11494     getLastDateOfMonth : function() {
11495         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11496     },
11497
11498     /**
11499      * Get the number of days in the current month, adjusted for leap year.
11500      * @return {Number} The number of days in the month.
11501      */
11502     getDaysInMonth: function() {
11503         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11504
11505         return function() { // return a closure for efficiency
11506             var m = this.getMonth();
11507
11508             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11509         }
11510     }(),
11511
11512     /**
11513      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11514      * @return {String} 'st, 'nd', 'rd' or 'th'.
11515      */
11516     getSuffix : function() {
11517         switch (this.getDate()) {
11518             case 1:
11519             case 21:
11520             case 31:
11521                 return "st";
11522             case 2:
11523             case 22:
11524                 return "nd";
11525             case 3:
11526             case 23:
11527                 return "rd";
11528             default:
11529                 return "th";
11530         }
11531     },
11532
11533     /**
11534      * Creates and returns a new Date instance with the exact same date value as the called instance.
11535      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11536      * variable will also be changed.  When the intention is to create a new variable that will not
11537      * modify the original instance, you should create a clone.
11538      *
11539      * Example of correctly cloning a date:
11540      * <pre><code>
11541 //wrong way:
11542 var orig = new Date('10/1/2006');
11543 var copy = orig;
11544 copy.setDate(5);
11545 document.write(orig);  //returns 'Thu Oct 05 2006'!
11546
11547 //correct way:
11548 var orig = new Date('10/1/2006');
11549 var copy = orig.clone();
11550 copy.setDate(5);
11551 document.write(orig);  //returns 'Thu Oct 01 2006'
11552 </code></pre>
11553      * @return {Date} The new Date instance.
11554      */
11555     clone : function() {
11556         return new Date(this.getTime());
11557     },
11558
11559     /**
11560      * Checks if the current date is affected by Daylight Saving Time (DST).
11561      * @return {Boolean} True if the current date is affected by DST.
11562      */
11563     isDST : function() {
11564         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11565         // courtesy of @geoffrey.mcgill
11566         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11567     },
11568
11569     /**
11570      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11571      * automatically adjusting for Daylight Saving Time (DST) where applicable.
11572      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11573      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11574      * @return {Date} this or the clone.
11575      */
11576     clearTime : function(clone) {
11577         if (clone) {
11578             return this.clone().clearTime();
11579         }
11580
11581         // get current date before clearing time
11582         var d = this.getDate();
11583
11584         // clear time
11585         this.setHours(0);
11586         this.setMinutes(0);
11587         this.setSeconds(0);
11588         this.setMilliseconds(0);
11589
11590         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11591             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11592             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11593
11594             // increment hour until cloned date == current date
11595             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11596
11597             this.setDate(d);
11598             this.setHours(c.getHours());
11599         }
11600
11601         return this;
11602     },
11603
11604     /**
11605      * Provides a convenient method for performing basic date arithmetic. This method
11606      * does not modify the Date instance being called - it creates and returns
11607      * a new Date instance containing the resulting date value.
11608      *
11609      * Examples:
11610      * <pre><code>
11611 // Basic usage:
11612 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11613 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11614
11615 // Negative values will be subtracted:
11616 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11617 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11618
11619 // You can even chain several calls together in one line:
11620 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11621 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11622 </code></pre>
11623      *
11624      * @param {String} interval A valid date interval enum value.
11625      * @param {Number} value The amount to add to the current date.
11626      * @return {Date} The new Date instance.
11627      */
11628     add : function(interval, value) {
11629         var d = this.clone();
11630         if (!interval || value === 0) return d;
11631
11632         switch(interval.toLowerCase()) {
11633             case Date.MILLI:
11634                 d.setMilliseconds(this.getMilliseconds() + value);
11635                 break;
11636             case Date.SECOND:
11637                 d.setSeconds(this.getSeconds() + value);
11638                 break;
11639             case Date.MINUTE:
11640                 d.setMinutes(this.getMinutes() + value);
11641                 break;
11642             case Date.HOUR:
11643                 d.setHours(this.getHours() + value);
11644                 break;
11645             case Date.DAY:
11646                 d.setDate(this.getDate() + value);
11647                 break;
11648             case Date.MONTH:
11649                 var day = this.getDate();
11650                 if (day > 28) {
11651                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11652                 }
11653                 d.setDate(day);
11654                 d.setMonth(this.getMonth() + value);
11655                 break;
11656             case Date.YEAR:
11657                 d.setFullYear(this.getFullYear() + value);
11658                 break;
11659         }
11660         return d;
11661     },
11662
11663     /**
11664      * Checks if this date falls on or between the given start and end dates.
11665      * @param {Date} start Start date
11666      * @param {Date} end End date
11667      * @return {Boolean} true if this date falls on or between the given start and end dates.
11668      */
11669     between : function(start, end) {
11670         var t = this.getTime();
11671         return start.getTime() <= t && t <= end.getTime();
11672     }
11673 });
11674
11675
11676 /**
11677  * Formats a date given the supplied format string.
11678  * @param {String} format The format string.
11679  * @return {String} The formatted date.
11680  * @method format
11681  */
11682 Date.prototype.format = Date.prototype.dateFormat;
11683
11684
11685 // private
11686 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11687     Ext.apply(Date.prototype, {
11688         _xMonth : Date.prototype.setMonth,
11689         _xDate  : Date.prototype.setDate,
11690
11691         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11692         // Date.setMonth does not work consistently if iMonth is not 0-11
11693         setMonth : function(num) {
11694             if (num <= -1) {
11695                 var n = Math.ceil(-num),
11696                     back_year = Math.ceil(n / 12),
11697                     month = (n % 12) ? 12 - n % 12 : 0;
11698
11699                 this.setFullYear(this.getFullYear() - back_year);
11700
11701                 return this._xMonth(month);
11702             } else {
11703                 return this._xMonth(num);
11704             }
11705         },
11706
11707         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11708         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11709         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11710         setDate : function(d) {
11711             // use setTime() to workaround setDate() bug
11712             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11713             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11714         }
11715     });
11716 }
11717
11718
11719
11720 /* Some basic Date tests... (requires Firebug)
11721
11722 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11723 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11724
11725 // standard tests
11726 console.group('Standard Date.parseDate() Tests');
11727     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11728     console.log('Date.parseDate("2009-02-04T12:37:55.001000", "c")        = %o', Date.parseDate("2009-02-04T12:37:55.001000", "c")); // assumes browser's timezone setting
11729     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11730     console.log('Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")   = %o', Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")); // GMT-0530
11731     console.log('Date.parseDate("2009-05-01T15:34:52,9876000+08:00", "c") = %o', Date.parseDate("2009-05-01T15:34:52,987600+08:00", "c")); // GMT+08:00
11732 console.groupEnd();
11733
11734 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11735 // -- accepts ALL 6 levels of date-time granularity
11736 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11737     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11738     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11739     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11740     console.log('Date.parseDate("1997-07-16T19:20+01:00", "c")            = %o', Date.parseDate("1997-07-16T19:20+01:00", "c")); // YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00)
11741     console.log('Date.parseDate("1997-07-16T19:20:30+01:00", "c")         = %o', Date.parseDate("1997-07-16T19:20:30+01:00", "c")); // YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:30+01:00)
11742     console.log('Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")      = %o', Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")); // YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
11743     console.log('Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")      = %o', Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")); // YYYY-MM-DD hh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
11744     console.log('Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)= %o', Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)); // strict date parsing with invalid month value
11745 console.groupEnd();
11746
11747 //*/
11748 /**
11749  * @class Ext.util.MixedCollection
11750  * @extends Ext.util.Observable
11751  * A Collection class that maintains both numeric indexes and keys and exposes events.
11752  * @constructor
11753  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11754  * function should add function references to the collection. Defaults to
11755  * <tt>false</tt>.
11756  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11757  * and return the key value for that item.  This is used when available to look up the key on items that
11758  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
11759  * equivalent to providing an implementation for the {@link #getKey} method.
11760  */
11761 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11762     this.items = [];
11763     this.map = {};
11764     this.keys = [];
11765     this.length = 0;
11766     this.addEvents(
11767         /**
11768          * @event clear
11769          * Fires when the collection is cleared.
11770          */
11771         'clear',
11772         /**
11773          * @event add
11774          * Fires when an item is added to the collection.
11775          * @param {Number} index The index at which the item was added.
11776          * @param {Object} o The item added.
11777          * @param {String} key The key associated with the added item.
11778          */
11779         'add',
11780         /**
11781          * @event replace
11782          * Fires when an item is replaced in the collection.
11783          * @param {String} key he key associated with the new added.
11784          * @param {Object} old The item being replaced.
11785          * @param {Object} new The new item.
11786          */
11787         'replace',
11788         /**
11789          * @event remove
11790          * Fires when an item is removed from the collection.
11791          * @param {Object} o The item being removed.
11792          * @param {String} key (optional) The key associated with the removed item.
11793          */
11794         'remove',
11795         'sort'
11796     );
11797     this.allowFunctions = allowFunctions === true;
11798     if(keyFn){
11799         this.getKey = keyFn;
11800     }
11801     Ext.util.MixedCollection.superclass.constructor.call(this);
11802 };
11803
11804 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11805
11806     /**
11807      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11808      * function should add function references to the collection. Defaults to
11809      * <tt>false</tt>.
11810      */
11811     allowFunctions : false,
11812
11813     /**
11814      * Adds an item to the collection. Fires the {@link #add} event when complete.
11815      * @param {String} key <p>The key to associate with the item, or the new item.</p>
11816      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11817      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11818      * the MixedCollection will be able to <i>derive</i> the key for the new item.
11819      * In this case just pass the new item in this parameter.</p>
11820      * @param {Object} o The item to add.
11821      * @return {Object} The item added.
11822      */
11823     add : function(key, o){
11824         if(arguments.length == 1){
11825             o = arguments[0];
11826             key = this.getKey(o);
11827         }
11828         if(typeof key != 'undefined' && key !== null){
11829             var old = this.map[key];
11830             if(typeof old != 'undefined'){
11831                 return this.replace(key, o);
11832             }
11833             this.map[key] = o;
11834         }
11835         this.length++;
11836         this.items.push(o);
11837         this.keys.push(key);
11838         this.fireEvent('add', this.length-1, o, key);
11839         return o;
11840     },
11841
11842     /**
11843       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
11844       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11845       * to return a different value as in the following examples:<pre><code>
11846 // normal way
11847 var mc = new Ext.util.MixedCollection();
11848 mc.add(someEl.dom.id, someEl);
11849 mc.add(otherEl.dom.id, otherEl);
11850 //and so on
11851
11852 // using getKey
11853 var mc = new Ext.util.MixedCollection();
11854 mc.getKey = function(el){
11855    return el.dom.id;
11856 };
11857 mc.add(someEl);
11858 mc.add(otherEl);
11859
11860 // or via the constructor
11861 var mc = new Ext.util.MixedCollection(false, function(el){
11862    return el.dom.id;
11863 });
11864 mc.add(someEl);
11865 mc.add(otherEl);
11866      * </code></pre>
11867      * @param {Object} item The item for which to find the key.
11868      * @return {Object} The key for the passed item.
11869      */
11870     getKey : function(o){
11871          return o.id;
11872     },
11873
11874     /**
11875      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11876      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11877      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11878      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11879      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11880      * with one having the same key value, then just pass the replacement item in this parameter.</p>
11881      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11882      * with that key.
11883      * @return {Object}  The new item.
11884      */
11885     replace : function(key, o){
11886         if(arguments.length == 1){
11887             o = arguments[0];
11888             key = this.getKey(o);
11889         }
11890         var old = this.map[key];
11891         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11892              return this.add(key, o);
11893         }
11894         var index = this.indexOfKey(key);
11895         this.items[index] = o;
11896         this.map[key] = o;
11897         this.fireEvent('replace', key, old, o);
11898         return o;
11899     },
11900
11901     /**
11902      * Adds all elements of an Array or an Object to the collection.
11903      * @param {Object/Array} objs An Object containing properties which will be added
11904      * to the collection, or an Array of values, each of which are added to the collection.
11905      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11906      * has been set to <tt>true</tt>.
11907      */
11908     addAll : function(objs){
11909         if(arguments.length > 1 || Ext.isArray(objs)){
11910             var args = arguments.length > 1 ? arguments : objs;
11911             for(var i = 0, len = args.length; i < len; i++){
11912                 this.add(args[i]);
11913             }
11914         }else{
11915             for(var key in objs){
11916                 if(this.allowFunctions || typeof objs[key] != 'function'){
11917                     this.add(key, objs[key]);
11918                 }
11919             }
11920         }
11921     },
11922
11923     /**
11924      * Executes the specified function once for every item in the collection, passing the following arguments:
11925      * <div class="mdetail-params"><ul>
11926      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11927      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11928      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11929      * </ul></div>
11930      * The function should return a boolean value. Returning false from the function will stop the iteration.
11931      * @param {Function} fn The function to execute for each item.
11932      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11933      */
11934     each : function(fn, scope){
11935         var items = [].concat(this.items); // each safe for removal
11936         for(var i = 0, len = items.length; i < len; i++){
11937             if(fn.call(scope || items[i], items[i], i, len) === false){
11938                 break;
11939             }
11940         }
11941     },
11942
11943     /**
11944      * Executes the specified function once for every key in the collection, passing each
11945      * key, and its associated item as the first two parameters.
11946      * @param {Function} fn The function to execute for each item.
11947      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11948      */
11949     eachKey : function(fn, scope){
11950         for(var i = 0, len = this.keys.length; i < len; i++){
11951             fn.call(scope || window, this.keys[i], this.items[i], i, len);
11952         }
11953     },
11954
11955     /**
11956      * Returns the first item in the collection which elicits a true return value from the
11957      * passed selection function.
11958      * @param {Function} fn The selection function to execute for each item.
11959      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11960      * @return {Object} The first item in the collection which returned true from the selection function.
11961      */
11962     find : function(fn, scope){
11963         for(var i = 0, len = this.items.length; i < len; i++){
11964             if(fn.call(scope || window, this.items[i], this.keys[i])){
11965                 return this.items[i];
11966             }
11967         }
11968         return null;
11969     },
11970
11971     /**
11972      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
11973      * @param {Number} index The index to insert the item at.
11974      * @param {String} key The key to associate with the new item, or the item itself.
11975      * @param {Object} o (optional) If the second parameter was a key, the new item.
11976      * @return {Object} The item inserted.
11977      */
11978     insert : function(index, key, o){
11979         if(arguments.length == 2){
11980             o = arguments[1];
11981             key = this.getKey(o);
11982         }
11983         if(this.containsKey(key)){
11984             this.suspendEvents();
11985             this.removeKey(key);
11986             this.resumeEvents();
11987         }
11988         if(index >= this.length){
11989             return this.add(key, o);
11990         }
11991         this.length++;
11992         this.items.splice(index, 0, o);
11993         if(typeof key != 'undefined' && key !== null){
11994             this.map[key] = o;
11995         }
11996         this.keys.splice(index, 0, key);
11997         this.fireEvent('add', index, o, key);
11998         return o;
11999     },
12000
12001     /**
12002      * Remove an item from the collection.
12003      * @param {Object} o The item to remove.
12004      * @return {Object} The item removed or false if no item was removed.
12005      */
12006     remove : function(o){
12007         return this.removeAt(this.indexOf(o));
12008     },
12009
12010     /**
12011      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12012      * @param {Number} index The index within the collection of the item to remove.
12013      * @return {Object} The item removed or false if no item was removed.
12014      */
12015     removeAt : function(index){
12016         if(index < this.length && index >= 0){
12017             this.length--;
12018             var o = this.items[index];
12019             this.items.splice(index, 1);
12020             var key = this.keys[index];
12021             if(typeof key != 'undefined'){
12022                 delete this.map[key];
12023             }
12024             this.keys.splice(index, 1);
12025             this.fireEvent('remove', o, key);
12026             return o;
12027         }
12028         return false;
12029     },
12030
12031     /**
12032      * Removed an item associated with the passed key fom the collection.
12033      * @param {String} key The key of the item to remove.
12034      * @return {Object} The item removed or false if no item was removed.
12035      */
12036     removeKey : function(key){
12037         return this.removeAt(this.indexOfKey(key));
12038     },
12039
12040     /**
12041      * Returns the number of items in the collection.
12042      * @return {Number} the number of items in the collection.
12043      */
12044     getCount : function(){
12045         return this.length;
12046     },
12047
12048     /**
12049      * Returns index within the collection of the passed Object.
12050      * @param {Object} o The item to find the index of.
12051      * @return {Number} index of the item. Returns -1 if not found.
12052      */
12053     indexOf : function(o){
12054         return this.items.indexOf(o);
12055     },
12056
12057     /**
12058      * Returns index within the collection of the passed key.
12059      * @param {String} key The key to find the index of.
12060      * @return {Number} index of the key.
12061      */
12062     indexOfKey : function(key){
12063         return this.keys.indexOf(key);
12064     },
12065
12066     /**
12067      * Returns the item associated with the passed key OR index.
12068      * Key has priority over index.  This is the equivalent
12069      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12070      * @param {String/Number} key The key or index of the item.
12071      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
12072      * If an item was found, but is a Class, returns <tt>null</tt>.
12073      */
12074     item : function(key){
12075         var mk = this.map[key],
12076             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12077         return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
12078     },
12079
12080     /**
12081      * Returns the item at the specified index.
12082      * @param {Number} index The index of the item.
12083      * @return {Object} The item at the specified index.
12084      */
12085     itemAt : function(index){
12086         return this.items[index];
12087     },
12088
12089     /**
12090      * Returns the item associated with the passed key.
12091      * @param {String/Number} key The key of the item.
12092      * @return {Object} The item associated with the passed key.
12093      */
12094     key : function(key){
12095         return this.map[key];
12096     },
12097
12098     /**
12099      * Returns true if the collection contains the passed Object as an item.
12100      * @param {Object} o  The Object to look for in the collection.
12101      * @return {Boolean} True if the collection contains the Object as an item.
12102      */
12103     contains : function(o){
12104         return this.indexOf(o) != -1;
12105     },
12106
12107     /**
12108      * Returns true if the collection contains the passed Object as a key.
12109      * @param {String} key The key to look for in the collection.
12110      * @return {Boolean} True if the collection contains the Object as a key.
12111      */
12112     containsKey : function(key){
12113         return typeof this.map[key] != 'undefined';
12114     },
12115
12116     /**
12117      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
12118      */
12119     clear : function(){
12120         this.length = 0;
12121         this.items = [];
12122         this.keys = [];
12123         this.map = {};
12124         this.fireEvent('clear');
12125     },
12126
12127     /**
12128      * Returns the first item in the collection.
12129      * @return {Object} the first item in the collection..
12130      */
12131     first : function(){
12132         return this.items[0];
12133     },
12134
12135     /**
12136      * Returns the last item in the collection.
12137      * @return {Object} the last item in the collection..
12138      */
12139     last : function(){
12140         return this.items[this.length-1];
12141     },
12142
12143     /**
12144      * @private
12145      * Performs the actual sorting based on a direction and a sorting function. Internally,
12146      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12147      * the sorted array data back into this.items and this.keys
12148      * @param {String} property Property to sort by ('key', 'value', or 'index')
12149      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12150      * @param {Function} fn (optional) Comparison function that defines the sort order.
12151      * Defaults to sorting by numeric value.
12152      */
12153     _sort : function(property, dir, fn){
12154         var i, len,
12155             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12156             
12157             //this is a temporary array used to apply the sorting function
12158             c     = [],
12159             keys  = this.keys,
12160             items = this.items;
12161         
12162         //default to a simple sorter function if one is not provided
12163         fn = fn || function(a, b) {
12164             return a - b;
12165         };
12166         
12167         //copy all the items into a temporary array, which we will sort
12168         for(i = 0, len = items.length; i < len; i++){
12169             c[c.length] = {
12170                 key  : keys[i], 
12171                 value: items[i], 
12172                 index: i
12173             };
12174         }
12175         
12176         //sort the temporary array
12177         c.sort(function(a, b){
12178             var v = fn(a[property], b[property]) * dsc;
12179             if(v === 0){
12180                 v = (a.index < b.index ? -1 : 1);
12181             }
12182             return v;
12183         });
12184         
12185         //copy the temporary array back into the main this.items and this.keys objects
12186         for(i = 0, len = c.length; i < len; i++){
12187             items[i] = c[i].value;
12188             keys[i]  = c[i].key;
12189         }
12190         
12191         this.fireEvent('sort', this);
12192     },
12193
12194     /**
12195      * Sorts this collection by <b>item</b> value with the passed comparison function.
12196      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12197      * @param {Function} fn (optional) Comparison function that defines the sort order.
12198      * Defaults to sorting by numeric value.
12199      */
12200     sort : function(dir, fn){
12201         this._sort('value', dir, fn);
12202     },
12203     
12204     /**
12205      * Reorders each of the items based on a mapping from old index to new index. Internally this
12206      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12207      * @param {Object} mapping Mapping from old item index to new item index
12208      */
12209     reorder: function(mapping) {
12210         this.suspendEvents();
12211         
12212         var items     = this.items,
12213             index     = 0,
12214             length    = items.length,
12215             order     = [],
12216             remaining = [];
12217         
12218         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12219         for (oldIndex in mapping) {
12220             order[mapping[oldIndex]] = items[oldIndex];
12221         } 
12222         
12223         for (index = 0; index < length; index++) {
12224             if (mapping[index] == undefined) {
12225                 remaining.push(items[index]);
12226             }
12227         }
12228         
12229         for (index = 0; index < length; index++) {
12230             if (order[index] == undefined) {
12231                 order[index] = remaining.shift();
12232             }
12233         }
12234         
12235         this.clear();
12236         this.addAll(order);
12237         
12238         this.resumeEvents();
12239         this.fireEvent('sort', this);
12240     },
12241
12242     /**
12243      * Sorts this collection by <b>key</b>s.
12244      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12245      * @param {Function} fn (optional) Comparison function that defines the sort order.
12246      * Defaults to sorting by case insensitive string.
12247      */
12248     keySort : function(dir, fn){
12249         this._sort('key', dir, fn || function(a, b){
12250             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12251             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12252         });
12253     },
12254
12255     /**
12256      * Returns a range of items in this collection
12257      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12258      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12259      * @return {Array} An array of items
12260      */
12261     getRange : function(start, end){
12262         var items = this.items;
12263         if(items.length < 1){
12264             return [];
12265         }
12266         start = start || 0;
12267         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12268         var i, r = [];
12269         if(start <= end){
12270             for(i = start; i <= end; i++) {
12271                 r[r.length] = items[i];
12272             }
12273         }else{
12274             for(i = start; i >= end; i--) {
12275                 r[r.length] = items[i];
12276             }
12277         }
12278         return r;
12279     },
12280
12281     /**
12282      * Filter the <i>objects</i> in this collection by a specific property.
12283      * Returns a new collection that has been filtered.
12284      * @param {String} property A property on your objects
12285      * @param {String/RegExp} value Either string that the property values
12286      * should start with or a RegExp to test against the property
12287      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12288      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12289      * @return {MixedCollection} The new filtered collection
12290      */
12291     filter : function(property, value, anyMatch, caseSensitive){
12292         if(Ext.isEmpty(value, false)){
12293             return this.clone();
12294         }
12295         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12296         return this.filterBy(function(o){
12297             return o && value.test(o[property]);
12298         });
12299     },
12300
12301     /**
12302      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12303      * The passed function will be called with each object in the collection.
12304      * If the function returns true, the value is included otherwise it is filtered.
12305      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12306      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12307      * @return {MixedCollection} The new filtered collection
12308      */
12309     filterBy : function(fn, scope){
12310         var r = new Ext.util.MixedCollection();
12311         r.getKey = this.getKey;
12312         var k = this.keys, it = this.items;
12313         for(var i = 0, len = it.length; i < len; i++){
12314             if(fn.call(scope||this, it[i], k[i])){
12315                 r.add(k[i], it[i]);
12316             }
12317         }
12318         return r;
12319     },
12320
12321     /**
12322      * Finds the index of the first matching object in this collection by a specific property/value.
12323      * @param {String} property The name of a property on your objects.
12324      * @param {String/RegExp} value A string that the property values
12325      * should start with or a RegExp to test against the property.
12326      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12327      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12328      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12329      * @return {Number} The matched index or -1
12330      */
12331     findIndex : function(property, value, start, anyMatch, caseSensitive){
12332         if(Ext.isEmpty(value, false)){
12333             return -1;
12334         }
12335         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12336         return this.findIndexBy(function(o){
12337             return o && value.test(o[property]);
12338         }, null, start);
12339     },
12340
12341     /**
12342      * Find the index of the first matching object in this collection by a function.
12343      * If the function returns <i>true</i> it is considered a match.
12344      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12345      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12346      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12347      * @return {Number} The matched index or -1
12348      */
12349     findIndexBy : function(fn, scope, start){
12350         var k = this.keys, it = this.items;
12351         for(var i = (start||0), len = it.length; i < len; i++){
12352             if(fn.call(scope||this, it[i], k[i])){
12353                 return i;
12354             }
12355         }
12356         return -1;
12357     },
12358
12359     /**
12360      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12361      * and by Ext.data.Store#filter
12362      * @private
12363      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12364      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12365      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12366      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12367      */
12368     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12369         if (!value.exec) { // not a regex
12370             var er = Ext.escapeRe;
12371             value = String(value);
12372             
12373             if (anyMatch === true) {
12374                 value = er(value);
12375             } else {
12376                 value = '^' + er(value);
12377                 if (exactMatch === true) {
12378                     value += '$';
12379                 }
12380             }
12381             value = new RegExp(value, caseSensitive ? '' : 'i');
12382          }
12383          return value;
12384     },
12385
12386     /**
12387      * Creates a shallow copy of this collection
12388      * @return {MixedCollection}
12389      */
12390     clone : function(){
12391         var r = new Ext.util.MixedCollection();
12392         var k = this.keys, it = this.items;
12393         for(var i = 0, len = it.length; i < len; i++){
12394             r.add(k[i], it[i]);
12395         }
12396         r.getKey = this.getKey;
12397         return r;
12398     }
12399 });
12400 /**
12401  * This method calls {@link #item item()}.
12402  * Returns the item associated with the passed key OR index. Key has priority
12403  * over index.  This is the equivalent of calling {@link #key} first, then if
12404  * nothing matched calling {@link #itemAt}.
12405  * @param {String/Number} key The key or index of the item.
12406  * @return {Object} If the item is found, returns the item.  If the item was
12407  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12408  * returns <tt>null</tt>.
12409  */
12410 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
12411  * @class Ext.util.JSON
12412  * Modified version of Douglas Crockford"s json.js that doesn"t
12413  * mess with the Object prototype
12414  * http://www.json.org/js.html
12415  * @singleton
12416  */
12417 Ext.util.JSON = new (function(){
12418     var useHasOwn = !!{}.hasOwnProperty,
12419         isNative = function() {
12420             var useNative = null;
12421
12422             return function() {
12423                 if (useNative === null) {
12424                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12425                 }
12426         
12427                 return useNative;
12428             };
12429         }(),
12430         pad = function(n) {
12431             return n < 10 ? "0" + n : n;
12432         },
12433         doDecode = function(json){
12434             return eval("(" + json + ')');    
12435         },
12436         doEncode = function(o){
12437             if(!Ext.isDefined(o) || o === null){
12438                 return "null";
12439             }else if(Ext.isArray(o)){
12440                 return encodeArray(o);
12441             }else if(Ext.isDate(o)){
12442                 return Ext.util.JSON.encodeDate(o);
12443             }else if(Ext.isString(o)){
12444                 return encodeString(o);
12445             }else if(typeof o == "number"){
12446                 //don't use isNumber here, since finite checks happen inside isNumber
12447                 return isFinite(o) ? String(o) : "null";
12448             }else if(Ext.isBoolean(o)){
12449                 return String(o);
12450             }else {
12451                 var a = ["{"], b, i, v;
12452                 for (i in o) {
12453                     // don't encode DOM objects
12454                     if(!o.getElementsByTagName){
12455                         if(!useHasOwn || o.hasOwnProperty(i)) {
12456                             v = o[i];
12457                             switch (typeof v) {
12458                             case "undefined":
12459                             case "function":
12460                             case "unknown":
12461                                 break;
12462                             default:
12463                                 if(b){
12464                                     a.push(',');
12465                                 }
12466                                 a.push(doEncode(i), ":",
12467                                         v === null ? "null" : doEncode(v));
12468                                 b = true;
12469                             }
12470                         }
12471                     }
12472                 }
12473                 a.push("}");
12474                 return a.join("");
12475             }    
12476         },
12477         m = {
12478             "\b": '\\b',
12479             "\t": '\\t',
12480             "\n": '\\n',
12481             "\f": '\\f',
12482             "\r": '\\r',
12483             '"' : '\\"',
12484             "\\": '\\\\'
12485         },
12486         encodeString = function(s){
12487             if (/["\\\x00-\x1f]/.test(s)) {
12488                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12489                     var c = m[b];
12490                     if(c){
12491                         return c;
12492                     }
12493                     c = b.charCodeAt();
12494                     return "\\u00" +
12495                         Math.floor(c / 16).toString(16) +
12496                         (c % 16).toString(16);
12497                 }) + '"';
12498             }
12499             return '"' + s + '"';
12500         },
12501         encodeArray = function(o){
12502             var a = ["["], b, i, l = o.length, v;
12503                 for (i = 0; i < l; i += 1) {
12504                     v = o[i];
12505                     switch (typeof v) {
12506                         case "undefined":
12507                         case "function":
12508                         case "unknown":
12509                             break;
12510                         default:
12511                             if (b) {
12512                                 a.push(',');
12513                             }
12514                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12515                             b = true;
12516                     }
12517                 }
12518                 a.push("]");
12519                 return a.join("");
12520         };
12521
12522     /**
12523      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12524      * <b>The returned value includes enclosing double quotation marks.</b></p>
12525      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12526      * <p>To override this:</p><pre><code>
12527 Ext.util.JSON.encodeDate = function(d) {
12528     return d.format('"Y-m-d"');
12529 };
12530 </code></pre>
12531      * @param {Date} d The Date to encode
12532      * @return {String} The string literal to use in a JSON string.
12533      */
12534     this.encodeDate = function(o){
12535         return '"' + o.getFullYear() + "-" +
12536                 pad(o.getMonth() + 1) + "-" +
12537                 pad(o.getDate()) + "T" +
12538                 pad(o.getHours()) + ":" +
12539                 pad(o.getMinutes()) + ":" +
12540                 pad(o.getSeconds()) + '"';
12541     };
12542
12543     /**
12544      * Encodes an Object, Array or other value
12545      * @param {Mixed} o The variable to encode
12546      * @return {String} The JSON string
12547      */
12548     this.encode = function() {
12549         var ec;
12550         return function(o) {
12551             if (!ec) {
12552                 // setup encoding function on first access
12553                 ec = isNative() ? JSON.stringify : doEncode;
12554             }
12555             return ec(o);
12556         };
12557     }();
12558
12559
12560     /**
12561      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12562      * @param {String} json The JSON string
12563      * @return {Object} The resulting object
12564      */
12565     this.decode = function() {
12566         var dc;
12567         return function(json) {
12568             if (!dc) {
12569                 // setup decoding function on first access
12570                 dc = isNative() ? JSON.parse : doDecode;
12571             }
12572             return dc(json);
12573         };
12574     }();
12575
12576 })();
12577 /**
12578  * Shorthand for {@link Ext.util.JSON#encode}
12579  * @param {Mixed} o The variable to encode
12580  * @return {String} The JSON string
12581  * @member Ext
12582  * @method encode
12583  */
12584 Ext.encode = Ext.util.JSON.encode;
12585 /**
12586  * Shorthand for {@link Ext.util.JSON#decode}
12587  * @param {String} json The JSON string
12588  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12589  * @return {Object} The resulting object
12590  * @member Ext
12591  * @method decode
12592  */
12593 Ext.decode = Ext.util.JSON.decode;
12594 /**
12595  * @class Ext.util.Format
12596  * Reusable data formatting functions
12597  * @singleton
12598  */
12599 Ext.util.Format = function(){
12600     var trimRe = /^\s+|\s+$/g,
12601         stripTagsRE = /<\/?[^>]+>/gi,
12602         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12603         nl2brRe = /\r?\n/g;
12604
12605     return {
12606         /**
12607          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12608          * @param {String} value The string to truncate
12609          * @param {Number} length The maximum length to allow before truncating
12610          * @param {Boolean} word True to try to find a common work break
12611          * @return {String} The converted text
12612          */
12613         ellipsis : function(value, len, word){
12614             if(value && value.length > len){
12615                 if(word){
12616                     var vs = value.substr(0, len - 2),
12617                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12618                     if(index == -1 || index < (len - 15)){
12619                         return value.substr(0, len - 3) + "...";
12620                     }else{
12621                         return vs.substr(0, index) + "...";
12622                     }
12623                 } else{
12624                     return value.substr(0, len - 3) + "...";
12625                 }
12626             }
12627             return value;
12628         },
12629
12630         /**
12631          * Checks a reference and converts it to empty string if it is undefined
12632          * @param {Mixed} value Reference to check
12633          * @return {Mixed} Empty string if converted, otherwise the original value
12634          */
12635         undef : function(value){
12636             return value !== undefined ? value : "";
12637         },
12638
12639         /**
12640          * Checks a reference and converts it to the default value if it's empty
12641          * @param {Mixed} value Reference to check
12642          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12643          * @return {String}
12644          */
12645         defaultValue : function(value, defaultValue){
12646             return value !== undefined && value !== '' ? value : defaultValue;
12647         },
12648
12649         /**
12650          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12651          * @param {String} value The string to encode
12652          * @return {String} The encoded text
12653          */
12654         htmlEncode : function(value){
12655             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12656         },
12657
12658         /**
12659          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12660          * @param {String} value The string to decode
12661          * @return {String} The decoded text
12662          */
12663         htmlDecode : function(value){
12664             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
12665         },
12666
12667         /**
12668          * Trims any whitespace from either side of a string
12669          * @param {String} value The text to trim
12670          * @return {String} The trimmed text
12671          */
12672         trim : function(value){
12673             return String(value).replace(trimRe, "");
12674         },
12675
12676         /**
12677          * Returns a substring from within an original string
12678          * @param {String} value The original text
12679          * @param {Number} start The start index of the substring
12680          * @param {Number} length The length of the substring
12681          * @return {String} The substring
12682          */
12683         substr : function(value, start, length){
12684             return String(value).substr(start, length);
12685         },
12686
12687         /**
12688          * Converts a string to all lower case letters
12689          * @param {String} value The text to convert
12690          * @return {String} The converted text
12691          */
12692         lowercase : function(value){
12693             return String(value).toLowerCase();
12694         },
12695
12696         /**
12697          * Converts a string to all upper case letters
12698          * @param {String} value The text to convert
12699          * @return {String} The converted text
12700          */
12701         uppercase : function(value){
12702             return String(value).toUpperCase();
12703         },
12704
12705         /**
12706          * Converts the first character only of a string to upper case
12707          * @param {String} value The text to convert
12708          * @return {String} The converted text
12709          */
12710         capitalize : function(value){
12711             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12712         },
12713
12714         // private
12715         call : function(value, fn){
12716             if(arguments.length > 2){
12717                 var args = Array.prototype.slice.call(arguments, 2);
12718                 args.unshift(value);
12719                 return eval(fn).apply(window, args);
12720             }else{
12721                 return eval(fn).call(window, value);
12722             }
12723         },
12724
12725         /**
12726          * Format a number as US currency
12727          * @param {Number/String} value The numeric value to format
12728          * @return {String} The formatted currency string
12729          */
12730         usMoney : function(v){
12731             v = (Math.round((v-0)*100))/100;
12732             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12733             v = String(v);
12734             var ps = v.split('.'),
12735                 whole = ps[0],
12736                 sub = ps[1] ? '.'+ ps[1] : '.00',
12737                 r = /(\d+)(\d{3})/;
12738             while (r.test(whole)) {
12739                 whole = whole.replace(r, '$1' + ',' + '$2');
12740             }
12741             v = whole + sub;
12742             if(v.charAt(0) == '-'){
12743                 return '-$' + v.substr(1);
12744             }
12745             return "$" +  v;
12746         },
12747
12748         /**
12749          * Parse a value into a formatted date using the specified format pattern.
12750          * @param {String/Date} value The value to format (Strings must conform to the format expected by the javascript Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method)
12751          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12752          * @return {String} The formatted date string
12753          */
12754         date : function(v, format){
12755             if(!v){
12756                 return "";
12757             }
12758             if(!Ext.isDate(v)){
12759                 v = new Date(Date.parse(v));
12760             }
12761             return v.dateFormat(format || "m/d/Y");
12762         },
12763
12764         /**
12765          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12766          * @param {String} format Any valid date format string
12767          * @return {Function} The date formatting function
12768          */
12769         dateRenderer : function(format){
12770             return function(v){
12771                 return Ext.util.Format.date(v, format);
12772             };
12773         },
12774
12775         /**
12776          * Strips all HTML tags
12777          * @param {Mixed} value The text from which to strip tags
12778          * @return {String} The stripped text
12779          */
12780         stripTags : function(v){
12781             return !v ? v : String(v).replace(stripTagsRE, "");
12782         },
12783
12784         /**
12785          * Strips all script tags
12786          * @param {Mixed} value The text from which to strip script tags
12787          * @return {String} The stripped text
12788          */
12789         stripScripts : function(v){
12790             return !v ? v : String(v).replace(stripScriptsRe, "");
12791         },
12792
12793         /**
12794          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12795          * @param {Number/String} size The numeric value to format
12796          * @return {String} The formatted file size
12797          */
12798         fileSize : function(size){
12799             if(size < 1024) {
12800                 return size + " bytes";
12801             } else if(size < 1048576) {
12802                 return (Math.round(((size*10) / 1024))/10) + " KB";
12803             } else {
12804                 return (Math.round(((size*10) / 1048576))/10) + " MB";
12805             }
12806         },
12807
12808         /**
12809          * It does simple math for use in a template, for example:<pre><code>
12810          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12811          * </code></pre>
12812          * @return {Function} A function that operates on the passed value.
12813          */
12814         math : function(){
12815             var fns = {};
12816             return function(v, a){
12817                 if(!fns[a]){
12818                     fns[a] = new Function('v', 'return v ' + a + ';');
12819                 }
12820                 return fns[a](v);
12821             }
12822         }(),
12823
12824         /**
12825          * Rounds the passed number to the required decimal precision.
12826          * @param {Number/String} value The numeric value to round.
12827          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12828          * @return {Number} The rounded value.
12829          */
12830         round : function(value, precision) {
12831             var result = Number(value);
12832             if (typeof precision == 'number') {
12833                 precision = Math.pow(10, precision);
12834                 result = Math.round(value * precision) / precision;
12835             }
12836             return result;
12837         },
12838
12839         /**
12840          * Formats the number according to the format string.
12841          * <div style="margin-left:40px">examples (123456.789):
12842          * <div style="margin-left:10px">
12843          * 0 - (123456) show only digits, no precision<br>
12844          * 0.00 - (123456.78) show only digits, 2 precision<br>
12845          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12846          * 0,000 - (123,456) show comma and digits, no precision<br>
12847          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12848          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12849          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12850          * For example: 0.000,00/i
12851          * </div></div>
12852          * @param {Number} v The number to format.
12853          * @param {String} format The way you would like to format this text.
12854          * @return {String} The formatted number.
12855          */
12856         number: function(v, format) {
12857             if(!format){
12858                 return v;
12859             }
12860             v = Ext.num(v, NaN);
12861             if (isNaN(v)){
12862                 return '';
12863             }
12864             var comma = ',',
12865                 dec = '.',
12866                 i18n = false,
12867                 neg = v < 0;
12868
12869             v = Math.abs(v);
12870             if(format.substr(format.length - 2) == '/i'){
12871                 format = format.substr(0, format.length - 2);
12872                 i18n = true;
12873                 comma = '.';
12874                 dec = ',';
12875             }
12876
12877             var hasComma = format.indexOf(comma) != -1,
12878                 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12879
12880             if(1 < psplit.length){
12881                 v = v.toFixed(psplit[1].length);
12882             }else if(2 < psplit.length){
12883                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12884             }else{
12885                 v = v.toFixed(0);
12886             }
12887
12888             var fnum = v.toString();
12889
12890             psplit = fnum.split('.');
12891
12892             if (hasComma) {
12893                 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12894
12895                 for (var i = 0; i < j; i += n) {
12896                     if (i != 0) {
12897                         n = 3;
12898                     }
12899                     parr[parr.length] = cnum.substr(i, n);
12900                     m -= 1;
12901                 }
12902                 fnum = parr.join(comma);
12903                 if (psplit[1]) {
12904                     fnum += dec + psplit[1];
12905                 }
12906             } else {
12907                 if (psplit[1]) {
12908                     fnum = psplit[0] + dec + psplit[1];
12909                 }
12910             }
12911
12912             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12913         },
12914
12915         /**
12916          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12917          * @param {String} format Any valid number format string for {@link #number}
12918          * @return {Function} The number formatting function
12919          */
12920         numberRenderer : function(format){
12921             return function(v){
12922                 return Ext.util.Format.number(v, format);
12923             };
12924         },
12925
12926         /**
12927          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
12928          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
12929          * if the value is 0 or greater than 1.
12930          * @param {Number} value The value to compare against
12931          * @param {String} singular The singular form of the word
12932          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
12933          */
12934         plural : function(v, s, p){
12935             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
12936         },
12937
12938         /**
12939          * Converts newline characters to the HTML tag &lt;br/>
12940          * @param {String} The string value to format.
12941          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
12942          */
12943         nl2br : function(v){
12944             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
12945         }
12946     }
12947 }();
12948 /**
12949  * @class Ext.XTemplate
12950  * @extends Ext.Template
12951  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12952  * <li>Autofilling arrays using templates and sub-templates</li>
12953  * <li>Conditional processing with basic comparison operators</li>
12954  * <li>Basic math function support</li>
12955  * <li>Execute arbitrary inline code with special built-in template variables</li>
12956  * <li>Custom member functions</li>
12957  * <li>Many special tags and built-in operators that aren't defined as part of
12958  * the API, but are supported in the templates that can be created</li>
12959  * </ul></div></p>
12960  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12961  * <li>{@link Ext.DataView}</li>
12962  * <li>{@link Ext.ListView}</li>
12963  * <li>{@link Ext.form.ComboBox}</li>
12964  * <li>{@link Ext.grid.TemplateColumn}</li>
12965  * <li>{@link Ext.grid.GroupingView}</li>
12966  * <li>{@link Ext.menu.Item}</li>
12967  * <li>{@link Ext.layout.MenuLayout}</li>
12968  * <li>{@link Ext.ColorPalette}</li>
12969  * </ul></div></p>
12970  * 
12971  * <p>For example usage {@link #XTemplate see the constructor}.</p>  
12972  *   
12973  * @constructor
12974  * The {@link Ext.Template#Template Ext.Template constructor} describes
12975  * the acceptable parameters to pass to the constructor. The following
12976  * examples demonstrate all of the supported features.</p>
12977  * 
12978  * <div class="mdetail-params"><ul>
12979  * 
12980  * <li><b><u>Sample Data</u></b> 
12981  * <div class="sub-desc">
12982  * <p>This is the data object used for reference in each code example:</p>
12983  * <pre><code>
12984 var data = {
12985     name: 'Jack Slocum',
12986     title: 'Lead Developer',
12987     company: 'Ext JS, LLC',
12988     email: 'jack@extjs.com',
12989     address: '4 Red Bulls Drive',
12990     city: 'Cleveland',
12991     state: 'Ohio',
12992     zip: '44102',
12993     drinks: ['Red Bull', 'Coffee', 'Water'],
12994     kids: [{
12995         name: 'Sara Grace',
12996         age:3
12997     },{
12998         name: 'Zachary',
12999         age:2
13000     },{
13001         name: 'John James',
13002         age:0
13003     }]
13004 };
13005  * </code></pre>
13006  * </div>
13007  * </li>
13008  * 
13009  * 
13010  * <li><b><u>Auto filling of arrays</u></b> 
13011  * <div class="sub-desc">
13012  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13013  * to process the provided data object:
13014  * <ul>
13015  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13016  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13017  * array.</li>
13018  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13019  * <li>While processing an array, the special variable <tt>{#}</tt>
13020  * will provide the current array index + 1 (starts at 1, not 0).</li>
13021  * </ul>
13022  * </p>
13023  * <pre><code>
13024 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
13025 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
13026 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
13027  * </code></pre>
13028  * Using the sample data above:
13029  * <pre><code>
13030 var tpl = new Ext.XTemplate(
13031     '&lt;p>Kids: ',
13032     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
13033         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
13034     '&lt;/tpl>&lt;/p>'
13035 );
13036 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13037  * </code></pre>
13038  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13039  * to access specified members of the provided data object to populate the template:</p>
13040  * <pre><code>
13041 var tpl = new Ext.XTemplate(
13042     '&lt;p>Name: {name}&lt;/p>',
13043     '&lt;p>Title: {title}&lt;/p>',
13044     '&lt;p>Company: {company}&lt;/p>',
13045     '&lt;p>Kids: ',
13046     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
13047         '&lt;p>{name}&lt;/p>',
13048     '&lt;/tpl>&lt;/p>'
13049 );
13050 tpl.overwrite(panel.body, data);  // pass the root node of the data object
13051  * </code></pre>
13052  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13053  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
13054  * will represent the value of the array at the current index:</p>
13055  * <pre><code>
13056 var tpl = new Ext.XTemplate(
13057     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
13058     '&lt;tpl for="drinks">',
13059        '&lt;div> - {.}&lt;/div>',
13060     '&lt;/tpl>'
13061 );
13062 tpl.overwrite(panel.body, data);
13063  * </code></pre>
13064  * <p>When processing a sub-template, for example while looping through a child array,
13065  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13066  * <pre><code>
13067 var tpl = new Ext.XTemplate(
13068     '&lt;p>Name: {name}&lt;/p>',
13069     '&lt;p>Kids: ',
13070     '&lt;tpl for="kids">',
13071         '&lt;tpl if="age > 1">',
13072             '&lt;p>{name}&lt;/p>',
13073             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
13074         '&lt;/tpl>',
13075     '&lt;/tpl>&lt;/p>'
13076 );
13077 tpl.overwrite(panel.body, data);
13078  * </code></pre>
13079  * </div>
13080  * </li>
13081  * 
13082  * 
13083  * <li><b><u>Conditional processing with basic comparison operators</u></b> 
13084  * <div class="sub-desc">
13085  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13086  * to provide conditional checks for deciding whether or not to render specific
13087  * parts of the template. Notes:<div class="sub-desc"><ul>
13088  * <li>Double quotes must be encoded if used within the conditional</li>
13089  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
13090  * <tt>if</tt> statements should be used.</li>
13091  * </ul></div>
13092  * <pre><code>
13093 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
13094 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
13095 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
13096 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
13097 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
13098 // no good:
13099 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
13100 // encode &#34; if it is part of the condition, e.g.
13101 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
13102  * </code></pre>
13103  * Using the sample data above:
13104  * <pre><code>
13105 var tpl = new Ext.XTemplate(
13106     '&lt;p>Name: {name}&lt;/p>',
13107     '&lt;p>Kids: ',
13108     '&lt;tpl for="kids">',
13109         '&lt;tpl if="age > 1">',
13110             '&lt;p>{name}&lt;/p>',
13111         '&lt;/tpl>',
13112     '&lt;/tpl>&lt;/p>'
13113 );
13114 tpl.overwrite(panel.body, data);
13115  * </code></pre>
13116  * </div>
13117  * </li>
13118  * 
13119  * 
13120  * <li><b><u>Basic math support</u></b> 
13121  * <div class="sub-desc">
13122  * <p>The following basic math operators may be applied directly on numeric
13123  * data values:</p><pre>
13124  * + - * /
13125  * </pre>
13126  * For example:
13127  * <pre><code>
13128 var tpl = new Ext.XTemplate(
13129     '&lt;p>Name: {name}&lt;/p>',
13130     '&lt;p>Kids: ',
13131     '&lt;tpl for="kids">',
13132         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
13133             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
13134             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
13135             '&lt;p>Dad: {parent.name}&lt;/p>',
13136         '&lt;/tpl>',
13137     '&lt;/tpl>&lt;/p>'
13138 );
13139 tpl.overwrite(panel.body, data);
13140 </code></pre>
13141  * </div>
13142  * </li>
13143  *
13144  * 
13145  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b> 
13146  * <div class="sub-desc">
13147  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13148  * in the scope of the template. There are some special variables available in that code:
13149  * <ul>
13150  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13151  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13152  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13153  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13154  * loop you are in (1-based).</li>
13155  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13156  * of the array you are looping.</li>
13157  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13158  * </ul>
13159  * This example demonstrates basic row striping using an inline code block and the
13160  * <tt>xindex</tt> variable:</p>
13161  * <pre><code>
13162 var tpl = new Ext.XTemplate(
13163     '&lt;p>Name: {name}&lt;/p>',
13164     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
13165     '&lt;p>Kids: ',
13166     '&lt;tpl for="kids">',
13167        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13168         '{name}',
13169         '&lt;/div>',
13170     '&lt;/tpl>&lt;/p>'
13171 );
13172 tpl.overwrite(panel.body, data);
13173  * </code></pre>
13174  * </div>
13175  * </li>
13176  * 
13177  * <li><b><u>Template member functions</u></b> 
13178  * <div class="sub-desc">
13179  * <p>One or more member functions can be specified in a configuration
13180  * object passed into the XTemplate constructor for more complex processing:</p>
13181  * <pre><code>
13182 var tpl = new Ext.XTemplate(
13183     '&lt;p>Name: {name}&lt;/p>',
13184     '&lt;p>Kids: ',
13185     '&lt;tpl for="kids">',
13186         '&lt;tpl if="this.isGirl(name)">',
13187             '&lt;p>Girl: {name} - {age}&lt;/p>',
13188         '&lt;/tpl>',
13189         // use opposite if statement to simulate 'else' processing:
13190         '&lt;tpl if="this.isGirl(name) == false">',
13191             '&lt;p>Boy: {name} - {age}&lt;/p>',
13192         '&lt;/tpl>',
13193         '&lt;tpl if="this.isBaby(age)">',
13194             '&lt;p>{name} is a baby!&lt;/p>',
13195         '&lt;/tpl>',
13196     '&lt;/tpl>&lt;/p>',
13197     {
13198         // XTemplate configuration:
13199         compiled: true,
13200         disableFormats: true,
13201         // member functions:
13202         isGirl: function(name){
13203             return name == 'Sara Grace';
13204         },
13205         isBaby: function(age){
13206             return age < 1;
13207         }
13208     }
13209 );
13210 tpl.overwrite(panel.body, data);
13211  * </code></pre>
13212  * </div>
13213  * </li>
13214  * 
13215  * </ul></div>
13216  * 
13217  * @param {Mixed} config
13218  */
13219 Ext.XTemplate = function(){
13220     Ext.XTemplate.superclass.constructor.apply(this, arguments);
13221
13222     var me = this,
13223         s = me.html,
13224         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13225         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13226         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13227         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13228         m,
13229         id = 0,
13230         tpls = [],
13231         VALUES = 'values',
13232         PARENT = 'parent',
13233         XINDEX = 'xindex',
13234         XCOUNT = 'xcount',
13235         RETURN = 'return ',
13236         WITHVALUES = 'with(values){ ';
13237
13238     s = ['<tpl>', s, '</tpl>'].join('');
13239
13240     while((m = s.match(re))){
13241         var m2 = m[0].match(nameRe),
13242                         m3 = m[0].match(ifRe),
13243                 m4 = m[0].match(execRe),
13244                 exp = null,
13245                 fn = null,
13246                 exec = null,
13247                 name = m2 && m2[1] ? m2[1] : '';
13248
13249        if (m3) {
13250            exp = m3 && m3[1] ? m3[1] : null;
13251            if(exp){
13252                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13253            }
13254        }
13255        if (m4) {
13256            exp = m4 && m4[1] ? m4[1] : null;
13257            if(exp){
13258                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13259            }
13260        }
13261        if(name){
13262            switch(name){
13263                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13264                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13265                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13266            }
13267        }
13268        tpls.push({
13269             id: id,
13270             target: name,
13271             exec: exec,
13272             test: fn,
13273             body: m[1]||''
13274         });
13275        s = s.replace(m[0], '{xtpl'+ id + '}');
13276        ++id;
13277     }
13278         Ext.each(tpls, function(t) {
13279         me.compileTpl(t);
13280     });
13281     me.master = tpls[tpls.length-1];
13282     me.tpls = tpls;
13283 };
13284 Ext.extend(Ext.XTemplate, Ext.Template, {
13285     // private
13286     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13287     // private
13288     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13289
13290     // private
13291     applySubTemplate : function(id, values, parent, xindex, xcount){
13292         var me = this,
13293                 len,
13294                 t = me.tpls[id],
13295                 vs,
13296                 buf = [];
13297         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13298             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13299             return '';
13300         }
13301         vs = t.target ? t.target.call(me, values, parent) : values;
13302         len = vs.length;
13303         parent = t.target ? values : parent;
13304         if(t.target && Ext.isArray(vs)){
13305                 Ext.each(vs, function(v, i) {
13306                 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
13307             });
13308             return buf.join('');
13309         }
13310         return t.compiled.call(me, vs, parent, xindex, xcount);
13311     },
13312
13313     // private
13314     compileTpl : function(tpl){
13315         var fm = Ext.util.Format,
13316                 useF = this.disableFormats !== true,
13317             sep = Ext.isGecko ? "+" : ",",
13318             body;
13319
13320         function fn(m, name, format, args, math){
13321             if(name.substr(0, 4) == 'xtpl'){
13322                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13323             }
13324             var v;
13325             if(name === '.'){
13326                 v = 'values';
13327             }else if(name === '#'){
13328                 v = 'xindex';
13329             }else if(name.indexOf('.') != -1){
13330                 v = name;
13331             }else{
13332                 v = "values['" + name + "']";
13333             }
13334             if(math){
13335                 v = '(' + v + math + ')';
13336             }
13337             if (format && useF) {
13338                 args = args ? ',' + args : "";
13339                 if(format.substr(0, 5) != "this."){
13340                     format = "fm." + format + '(';
13341                 }else{
13342                     format = 'this.call("'+ format.substr(5) + '", ';
13343                     args = ", values";
13344                 }
13345             } else {
13346                 args= ''; format = "("+v+" === undefined ? '' : ";
13347             }
13348             return "'"+ sep + format + v + args + ")"+sep+"'";
13349         }
13350
13351         function codeFn(m, code){
13352             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13353             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13354         }
13355
13356         // branched to use + in gecko and [].join() in others
13357         if(Ext.isGecko){
13358             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13359                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13360                     "';};";
13361         }else{
13362             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13363             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13364             body.push("'].join('');};");
13365             body = body.join('');
13366         }
13367         eval(body);
13368         return this;
13369     },
13370
13371     /**
13372      * Returns an HTML fragment of this template with the specified values applied.
13373      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
13374      * @return {String} The HTML fragment
13375      */
13376     applyTemplate : function(values){
13377         return this.master.compiled.call(this, values, {}, 1, 1);
13378     },
13379
13380     /**
13381      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
13382      * @return {Function} The compiled function
13383      */
13384     compile : function(){return this;}
13385
13386     /**
13387      * @property re
13388      * @hide
13389      */
13390     /**
13391      * @property disableFormats
13392      * @hide
13393      */
13394     /**
13395      * @method set
13396      * @hide
13397      */
13398
13399 });
13400 /**
13401  * Alias for {@link #applyTemplate}
13402  * Returns an HTML fragment of this template with the specified values applied.
13403  * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
13404  * @return {String} The HTML fragment
13405  * @member Ext.XTemplate
13406  * @method apply
13407  */
13408 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13409
13410 /**
13411  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13412  * @param {String/HTMLElement} el A DOM element or its id
13413  * @return {Ext.Template} The created template
13414  * @static
13415  */
13416 Ext.XTemplate.from = function(el){
13417     el = Ext.getDom(el);
13418     return new Ext.XTemplate(el.value || el.innerHTML);
13419 };/**
13420  * @class Ext.util.CSS
13421  * Utility class for manipulating CSS rules
13422  * @singleton
13423  */
13424 Ext.util.CSS = function(){
13425         var rules = null;
13426         var doc = document;
13427
13428     var camelRe = /(-[a-z])/gi;
13429     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13430
13431    return {
13432    /**
13433     * Creates a stylesheet from a text blob of rules.
13434     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13435     * @param {String} cssText The text containing the css rules
13436     * @param {String} id An id to add to the stylesheet for later removal
13437     * @return {StyleSheet}
13438     */
13439    createStyleSheet : function(cssText, id){
13440        var ss;
13441        var head = doc.getElementsByTagName("head")[0];
13442        var rules = doc.createElement("style");
13443        rules.setAttribute("type", "text/css");
13444        if(id){
13445            rules.setAttribute("id", id);
13446        }
13447        if(Ext.isIE){
13448            head.appendChild(rules);
13449            ss = rules.styleSheet;
13450            ss.cssText = cssText;
13451        }else{
13452            try{
13453                 rules.appendChild(doc.createTextNode(cssText));
13454            }catch(e){
13455                rules.cssText = cssText;
13456            }
13457            head.appendChild(rules);
13458            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13459        }
13460        this.cacheStyleSheet(ss);
13461        return ss;
13462    },
13463
13464    /**
13465     * Removes a style or link tag by id
13466     * @param {String} id The id of the tag
13467     */
13468    removeStyleSheet : function(id){
13469        var existing = doc.getElementById(id);
13470        if(existing){
13471            existing.parentNode.removeChild(existing);
13472        }
13473    },
13474
13475    /**
13476     * Dynamically swaps an existing stylesheet reference for a new one
13477     * @param {String} id The id of an existing link tag to remove
13478     * @param {String} url The href of the new stylesheet to include
13479     */
13480    swapStyleSheet : function(id, url){
13481        this.removeStyleSheet(id);
13482        var ss = doc.createElement("link");
13483        ss.setAttribute("rel", "stylesheet");
13484        ss.setAttribute("type", "text/css");
13485        ss.setAttribute("id", id);
13486        ss.setAttribute("href", url);
13487        doc.getElementsByTagName("head")[0].appendChild(ss);
13488    },
13489    
13490    /**
13491     * Refresh the rule cache if you have dynamically added stylesheets
13492     * @return {Object} An object (hash) of rules indexed by selector
13493     */
13494    refreshCache : function(){
13495        return this.getRules(true);
13496    },
13497
13498    // private
13499    cacheStyleSheet : function(ss){
13500        if(!rules){
13501            rules = {};
13502        }
13503        try{// try catch for cross domain access issue
13504            var ssRules = ss.cssRules || ss.rules;
13505            for(var j = ssRules.length-1; j >= 0; --j){
13506                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13507            }
13508        }catch(e){}
13509    },
13510    
13511    /**
13512     * Gets all css rules for the document
13513     * @param {Boolean} refreshCache true to refresh the internal cache
13514     * @return {Object} An object (hash) of rules indexed by selector
13515     */
13516    getRules : function(refreshCache){
13517                 if(rules === null || refreshCache){
13518                         rules = {};
13519                         var ds = doc.styleSheets;
13520                         for(var i =0, len = ds.length; i < len; i++){
13521                             try{
13522                         this.cacheStyleSheet(ds[i]);
13523                     }catch(e){} 
13524                 }
13525                 }
13526                 return rules;
13527         },
13528         
13529         /**
13530     * Gets an an individual CSS rule by selector(s)
13531     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13532     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13533     * @return {CSSRule} The CSS rule or null if one is not found
13534     */
13535    getRule : function(selector, refreshCache){
13536                 var rs = this.getRules(refreshCache);
13537                 if(!Ext.isArray(selector)){
13538                     return rs[selector.toLowerCase()];
13539                 }
13540                 for(var i = 0; i < selector.length; i++){
13541                         if(rs[selector[i]]){
13542                                 return rs[selector[i].toLowerCase()];
13543                         }
13544                 }
13545                 return null;
13546         },
13547         
13548         
13549         /**
13550     * Updates a rule property
13551     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13552     * @param {String} property The css property
13553     * @param {String} value The new value for the property
13554     * @return {Boolean} true If a rule was found and updated
13555     */
13556    updateRule : function(selector, property, value){
13557                 if(!Ext.isArray(selector)){
13558                         var rule = this.getRule(selector);
13559                         if(rule){
13560                                 rule.style[property.replace(camelRe, camelFn)] = value;
13561                                 return true;
13562                         }
13563                 }else{
13564                         for(var i = 0; i < selector.length; i++){
13565                                 if(this.updateRule(selector[i], property, value)){
13566                                         return true;
13567                                 }
13568                         }
13569                 }
13570                 return false;
13571         }
13572    };   
13573 }();/**
13574  @class Ext.util.ClickRepeater
13575  @extends Ext.util.Observable
13576
13577  A wrapper class which can be applied to any element. Fires a "click" event while the
13578  mouse is pressed. The interval between firings may be specified in the config but
13579  defaults to 20 milliseconds.
13580
13581  Optionally, a CSS class may be applied to the element during the time it is pressed.
13582
13583  @cfg {Mixed} el The element to act as a button.
13584  @cfg {Number} delay The initial delay before the repeating event begins firing.
13585  Similar to an autorepeat key delay.
13586  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13587  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13588  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13589            "interval" and "delay" are ignored.
13590  @cfg {Boolean} preventDefault True to prevent the default click event
13591  @cfg {Boolean} stopDefault True to stop the default click event
13592
13593  @history
13594     2007-02-02 jvs Original code contributed by Nige "Animal" White
13595     2007-02-02 jvs Renamed to ClickRepeater
13596     2007-02-03 jvs Modifications for FF Mac and Safari
13597
13598  @constructor
13599  @param {Mixed} el The element to listen on
13600  @param {Object} config
13601  */
13602 Ext.util.ClickRepeater = function(el, config)
13603 {
13604     this.el = Ext.get(el);
13605     this.el.unselectable();
13606
13607     Ext.apply(this, config);
13608
13609     this.addEvents(
13610     /**
13611      * @event mousedown
13612      * Fires when the mouse button is depressed.
13613      * @param {Ext.util.ClickRepeater} this
13614      */
13615         "mousedown",
13616     /**
13617      * @event click
13618      * Fires on a specified interval during the time the element is pressed.
13619      * @param {Ext.util.ClickRepeater} this
13620      */
13621         "click",
13622     /**
13623      * @event mouseup
13624      * Fires when the mouse key is released.
13625      * @param {Ext.util.ClickRepeater} this
13626      */
13627         "mouseup"
13628     );
13629
13630     if(!this.disabled){
13631         this.disabled = true;
13632         this.enable();
13633     }
13634
13635     // allow inline handler
13636     if(this.handler){
13637         this.on("click", this.handler,  this.scope || this);
13638     }
13639
13640     Ext.util.ClickRepeater.superclass.constructor.call(this);
13641 };
13642
13643 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13644     interval : 20,
13645     delay: 250,
13646     preventDefault : true,
13647     stopDefault : false,
13648     timer : 0,
13649
13650     /**
13651      * Enables the repeater and allows events to fire.
13652      */
13653     enable: function(){
13654         if(this.disabled){
13655             this.el.on('mousedown', this.handleMouseDown, this);
13656             if (Ext.isIE){
13657                 this.el.on('dblclick', this.handleDblClick, this);
13658             }
13659             if(this.preventDefault || this.stopDefault){
13660                 this.el.on('click', this.eventOptions, this);
13661             }
13662         }
13663         this.disabled = false;
13664     },
13665
13666     /**
13667      * Disables the repeater and stops events from firing.
13668      */
13669     disable: function(/* private */ force){
13670         if(force || !this.disabled){
13671             clearTimeout(this.timer);
13672             if(this.pressClass){
13673                 this.el.removeClass(this.pressClass);
13674             }
13675             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13676             this.el.removeAllListeners();
13677         }
13678         this.disabled = true;
13679     },
13680
13681     /**
13682      * Convenience function for setting disabled/enabled by boolean.
13683      * @param {Boolean} disabled
13684      */
13685     setDisabled: function(disabled){
13686         this[disabled ? 'disable' : 'enable']();
13687     },
13688
13689     eventOptions: function(e){
13690         if(this.preventDefault){
13691             e.preventDefault();
13692         }
13693         if(this.stopDefault){
13694             e.stopEvent();
13695         }
13696     },
13697
13698     // private
13699     destroy : function() {
13700         this.disable(true);
13701         Ext.destroy(this.el);
13702         this.purgeListeners();
13703     },
13704
13705     handleDblClick : function(){
13706         clearTimeout(this.timer);
13707         this.el.blur();
13708
13709         this.fireEvent("mousedown", this);
13710         this.fireEvent("click", this);
13711     },
13712
13713     // private
13714     handleMouseDown : function(){
13715         clearTimeout(this.timer);
13716         this.el.blur();
13717         if(this.pressClass){
13718             this.el.addClass(this.pressClass);
13719         }
13720         this.mousedownTime = new Date();
13721
13722         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13723         this.el.on("mouseout", this.handleMouseOut, this);
13724
13725         this.fireEvent("mousedown", this);
13726         this.fireEvent("click", this);
13727
13728         // Do not honor delay or interval if acceleration wanted.
13729         if (this.accelerate) {
13730             this.delay = 400;
13731         }
13732         this.timer = this.click.defer(this.delay || this.interval, this);
13733     },
13734
13735     // private
13736     click : function(){
13737         this.fireEvent("click", this);
13738         this.timer = this.click.defer(this.accelerate ?
13739             this.easeOutExpo(this.mousedownTime.getElapsed(),
13740                 400,
13741                 -390,
13742                 12000) :
13743             this.interval, this);
13744     },
13745
13746     easeOutExpo : function (t, b, c, d) {
13747         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13748     },
13749
13750     // private
13751     handleMouseOut : function(){
13752         clearTimeout(this.timer);
13753         if(this.pressClass){
13754             this.el.removeClass(this.pressClass);
13755         }
13756         this.el.on("mouseover", this.handleMouseReturn, this);
13757     },
13758
13759     // private
13760     handleMouseReturn : function(){
13761         this.el.un("mouseover", this.handleMouseReturn, this);
13762         if(this.pressClass){
13763             this.el.addClass(this.pressClass);
13764         }
13765         this.click();
13766     },
13767
13768     // private
13769     handleMouseUp : function(){
13770         clearTimeout(this.timer);
13771         this.el.un("mouseover", this.handleMouseReturn, this);
13772         this.el.un("mouseout", this.handleMouseOut, this);
13773         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13774         this.el.removeClass(this.pressClass);
13775         this.fireEvent("mouseup", this);
13776     }
13777 });/**
13778  * @class Ext.KeyNav
13779  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13780  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13781  * way to implement custom navigation schemes for any UI component.</p>
13782  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13783  * pageUp, pageDown, del, home, end.  Usage:</p>
13784  <pre><code>
13785 var nav = new Ext.KeyNav("my-element", {
13786     "left" : function(e){
13787         this.moveLeft(e.ctrlKey);
13788     },
13789     "right" : function(e){
13790         this.moveRight(e.ctrlKey);
13791     },
13792     "enter" : function(e){
13793         this.save();
13794     },
13795     scope : this
13796 });
13797 </code></pre>
13798  * @constructor
13799  * @param {Mixed} el The element to bind to
13800  * @param {Object} config The config
13801  */
13802 Ext.KeyNav = function(el, config){
13803     this.el = Ext.get(el);
13804     Ext.apply(this, config);
13805     if(!this.disabled){
13806         this.disabled = true;
13807         this.enable();
13808     }
13809 };
13810
13811 Ext.KeyNav.prototype = {
13812     /**
13813      * @cfg {Boolean} disabled
13814      * True to disable this KeyNav instance (defaults to false)
13815      */
13816     disabled : false,
13817     /**
13818      * @cfg {String} defaultEventAction
13819      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13820      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13821      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13822      */
13823     defaultEventAction: "stopEvent",
13824     /**
13825      * @cfg {Boolean} forceKeyDown
13826      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13827      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13828      * handle keydown instead of keypress.
13829      */
13830     forceKeyDown : false,
13831
13832     // private
13833     relay : function(e){
13834         var k = e.getKey();
13835         var h = this.keyToHandler[k];
13836         if(h && this[h]){
13837             if(this.doRelay(e, this[h], h) !== true){
13838                 e[this.defaultEventAction]();
13839             }
13840         }
13841     },
13842
13843     // private
13844     doRelay : function(e, h, hname){
13845         return h.call(this.scope || this, e);
13846     },
13847
13848     // possible handlers
13849     enter : false,
13850     left : false,
13851     right : false,
13852     up : false,
13853     down : false,
13854     tab : false,
13855     esc : false,
13856     pageUp : false,
13857     pageDown : false,
13858     del : false,
13859     home : false,
13860     end : false,
13861
13862     // quick lookup hash
13863     keyToHandler : {
13864         37 : "left",
13865         39 : "right",
13866         38 : "up",
13867         40 : "down",
13868         33 : "pageUp",
13869         34 : "pageDown",
13870         46 : "del",
13871         36 : "home",
13872         35 : "end",
13873         13 : "enter",
13874         27 : "esc",
13875         9  : "tab"
13876     },
13877     
13878     stopKeyUp: function(e) {
13879         var k = e.getKey();
13880
13881         if (k >= 37 && k <= 40) {
13882             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13883             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13884             e.stopEvent();
13885         }
13886     },
13887     
13888     /**
13889      * Destroy this KeyNav (this is the same as calling disable).
13890      */
13891     destroy: function(){
13892         this.disable();    
13893     },
13894
13895         /**
13896          * Enable this KeyNav
13897          */
13898         enable: function() {
13899         if (this.disabled) {
13900             if (Ext.isSafari2) {
13901                 // call stopKeyUp() on "keyup" event
13902                 this.el.on('keyup', this.stopKeyUp, this);
13903             }
13904
13905             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13906             this.disabled = false;
13907         }
13908     },
13909
13910         /**
13911          * Disable this KeyNav
13912          */
13913         disable: function() {
13914         if (!this.disabled) {
13915             if (Ext.isSafari2) {
13916                 // remove "keyup" event handler
13917                 this.el.un('keyup', this.stopKeyUp, this);
13918             }
13919
13920             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13921             this.disabled = true;
13922         }
13923     },
13924     
13925     /**
13926      * Convenience function for setting disabled/enabled by boolean.
13927      * @param {Boolean} disabled
13928      */
13929     setDisabled : function(disabled){
13930         this[disabled ? "disable" : "enable"]();
13931     },
13932     
13933     // private
13934     isKeydown: function(){
13935         return this.forceKeyDown || Ext.EventManager.useKeydown;
13936     }
13937 };
13938 /**
13939  * @class Ext.KeyMap
13940  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13941  * The constructor accepts the same config object as defined by {@link #addBinding}.
13942  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13943  * combination it will call the function with this signature (if the match is a multi-key
13944  * combination the callback will still be called only once): (String key, Ext.EventObject e)
13945  * A KeyMap can also handle a string representation of keys.<br />
13946  * Usage:
13947  <pre><code>
13948 // map one key by key code
13949 var map = new Ext.KeyMap("my-element", {
13950     key: 13, // or Ext.EventObject.ENTER
13951     fn: myHandler,
13952     scope: myObject
13953 });
13954
13955 // map multiple keys to one action by string
13956 var map = new Ext.KeyMap("my-element", {
13957     key: "a\r\n\t",
13958     fn: myHandler,
13959     scope: myObject
13960 });
13961
13962 // map multiple keys to multiple actions by strings and array of codes
13963 var map = new Ext.KeyMap("my-element", [
13964     {
13965         key: [10,13],
13966         fn: function(){ alert("Return was pressed"); }
13967     }, {
13968         key: "abc",
13969         fn: function(){ alert('a, b or c was pressed'); }
13970     }, {
13971         key: "\t",
13972         ctrl:true,
13973         shift:true,
13974         fn: function(){ alert('Control + shift + tab was pressed.'); }
13975     }
13976 ]);
13977 </code></pre>
13978  * <b>Note: A KeyMap starts enabled</b>
13979  * @constructor
13980  * @param {Mixed} el The element to bind to
13981  * @param {Object} config The config (see {@link #addBinding})
13982  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13983  */
13984 Ext.KeyMap = function(el, config, eventName){
13985     this.el  = Ext.get(el);
13986     this.eventName = eventName || "keydown";
13987     this.bindings = [];
13988     if(config){
13989         this.addBinding(config);
13990     }
13991     this.enable();
13992 };
13993
13994 Ext.KeyMap.prototype = {
13995     /**
13996      * True to stop the event from bubbling and prevent the default browser action if the
13997      * key was handled by the KeyMap (defaults to false)
13998      * @type Boolean
13999      */
14000     stopEvent : false,
14001
14002     /**
14003      * Add a new binding to this KeyMap. The following config object properties are supported:
14004      * <pre>
14005 Property    Type             Description
14006 ----------  ---------------  ----------------------------------------------------------------------
14007 key         String/Array     A single keycode or an array of keycodes to handle
14008 shift       Boolean          True to handle key only when shift is pressed, False to handle the key only when shift is not pressed (defaults to undefined)
14009 ctrl        Boolean          True to handle key only when ctrl is pressed, False to handle the key only when ctrl is not pressed (defaults to undefined)
14010 alt         Boolean          True to handle key only when alt is pressed, False to handle the key only when alt is not pressed (defaults to undefined)
14011 handler     Function         The function to call when KeyMap finds the expected key combination
14012 fn          Function         Alias of handler (for backwards-compatibility)
14013 scope       Object           The scope of the callback function
14014 stopEvent   Boolean          True to stop the event from bubbling and prevent the default browser action if the key was handled by the KeyMap (defaults to false)
14015 </pre>
14016      *
14017      * Usage:
14018      * <pre><code>
14019 // Create a KeyMap
14020 var map = new Ext.KeyMap(document, {
14021     key: Ext.EventObject.ENTER,
14022     fn: handleKey,
14023     scope: this
14024 });
14025
14026 //Add a new binding to the existing KeyMap later
14027 map.addBinding({
14028     key: 'abc',
14029     shift: true,
14030     fn: handleKey,
14031     scope: this
14032 });
14033 </code></pre>
14034      * @param {Object/Array} config A single KeyMap config or an array of configs
14035      */
14036         addBinding : function(config){
14037         if(Ext.isArray(config)){
14038             Ext.each(config, function(c){
14039                 this.addBinding(c);
14040             }, this);
14041             return;
14042         }
14043         var keyCode = config.key,
14044             fn = config.fn || config.handler,
14045             scope = config.scope;
14046
14047         if (config.stopEvent) {
14048             this.stopEvent = config.stopEvent;    
14049         }       
14050
14051         if(typeof keyCode == "string"){
14052             var ks = [];
14053             var keyString = keyCode.toUpperCase();
14054             for(var j = 0, len = keyString.length; j < len; j++){
14055                 ks.push(keyString.charCodeAt(j));
14056             }
14057             keyCode = ks;
14058         }
14059         var keyArray = Ext.isArray(keyCode);
14060         
14061         var handler = function(e){
14062             if(this.checkModifiers(config, e)){
14063                 var k = e.getKey();
14064                 if(keyArray){
14065                     for(var i = 0, len = keyCode.length; i < len; i++){
14066                         if(keyCode[i] == k){
14067                           if(this.stopEvent){
14068                               e.stopEvent();
14069                           }
14070                           fn.call(scope || window, k, e);
14071                           return;
14072                         }
14073                     }
14074                 }else{
14075                     if(k == keyCode){
14076                         if(this.stopEvent){
14077                            e.stopEvent();
14078                         }
14079                         fn.call(scope || window, k, e);
14080                     }
14081                 }
14082             }
14083         };
14084         this.bindings.push(handler);
14085         },
14086     
14087     // private
14088     checkModifiers: function(config, e){
14089         var val, key, keys = ['shift', 'ctrl', 'alt'];
14090         for (var i = 0, len = keys.length; i < len; ++i){
14091             key = keys[i];
14092             val = config[key];
14093             if(!(val === undefined || (val === e[key + 'Key']))){
14094                 return false;
14095             }
14096         }
14097         return true;
14098     },
14099
14100     /**
14101      * Shorthand for adding a single key listener
14102      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14103      * following options:
14104      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14105      * @param {Function} fn The function to call
14106      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14107      */
14108     on : function(key, fn, scope){
14109         var keyCode, shift, ctrl, alt;
14110         if(typeof key == "object" && !Ext.isArray(key)){
14111             keyCode = key.key;
14112             shift = key.shift;
14113             ctrl = key.ctrl;
14114             alt = key.alt;
14115         }else{
14116             keyCode = key;
14117         }
14118         this.addBinding({
14119             key: keyCode,
14120             shift: shift,
14121             ctrl: ctrl,
14122             alt: alt,
14123             fn: fn,
14124             scope: scope
14125         });
14126     },
14127
14128     // private
14129     handleKeyDown : function(e){
14130             if(this.enabled){ //just in case
14131             var b = this.bindings;
14132             for(var i = 0, len = b.length; i < len; i++){
14133                 b[i].call(this, e);
14134             }
14135             }
14136         },
14137
14138         /**
14139          * Returns true if this KeyMap is enabled
14140          * @return {Boolean}
14141          */
14142         isEnabled : function(){
14143             return this.enabled;
14144         },
14145
14146         /**
14147          * Enables this KeyMap
14148          */
14149         enable: function(){
14150                 if(!this.enabled){
14151                     this.el.on(this.eventName, this.handleKeyDown, this);
14152                     this.enabled = true;
14153                 }
14154         },
14155
14156         /**
14157          * Disable this KeyMap
14158          */
14159         disable: function(){
14160                 if(this.enabled){
14161                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14162                     this.enabled = false;
14163                 }
14164         },
14165     
14166     /**
14167      * Convenience function for setting disabled/enabled by boolean.
14168      * @param {Boolean} disabled
14169      */
14170     setDisabled : function(disabled){
14171         this[disabled ? "disable" : "enable"]();
14172     }
14173 };/**
14174  * @class Ext.util.TextMetrics
14175  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14176  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14177  * should not contain any HTML, otherwise it may not be measured correctly.
14178  * @singleton
14179  */
14180 Ext.util.TextMetrics = function(){
14181     var shared;
14182     return {
14183         /**
14184          * Measures the size of the specified text
14185          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14186          * that can affect the size of the rendered text
14187          * @param {String} text The text to measure
14188          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14189          * in order to accurately measure the text height
14190          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14191          */
14192         measure : function(el, text, fixedWidth){
14193             if(!shared){
14194                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14195             }
14196             shared.bind(el);
14197             shared.setFixedWidth(fixedWidth || 'auto');
14198             return shared.getSize(text);
14199         },
14200
14201         /**
14202          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14203          * the overhead of multiple calls to initialize the style properties on each measurement.
14204          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14205          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14206          * in order to accurately measure the text height
14207          * @return {Ext.util.TextMetrics.Instance} instance The new instance
14208          */
14209         createInstance : function(el, fixedWidth){
14210             return Ext.util.TextMetrics.Instance(el, fixedWidth);
14211         }
14212     };
14213 }();
14214
14215 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14216     var ml = new Ext.Element(document.createElement('div'));
14217     document.body.appendChild(ml.dom);
14218     ml.position('absolute');
14219     ml.setLeftTop(-1000, -1000);
14220     ml.hide();
14221
14222     if(fixedWidth){
14223         ml.setWidth(fixedWidth);
14224     }
14225
14226     var instance = {
14227         /**
14228          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14229          * Returns the size of the specified text based on the internal element's style and width properties
14230          * @param {String} text The text to measure
14231          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14232          */
14233         getSize : function(text){
14234             ml.update(text);
14235             var s = ml.getSize();
14236             ml.update('');
14237             return s;
14238         },
14239
14240         /**
14241          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14242          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14243          * that can affect the size of the rendered text
14244          * @param {String/HTMLElement} el The element, dom node or id
14245          */
14246         bind : function(el){
14247             ml.setStyle(
14248                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14249             );
14250         },
14251
14252         /**
14253          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14254          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14255          * to set a fixed width in order to accurately measure the text height.
14256          * @param {Number} width The width to set on the element
14257          */
14258         setFixedWidth : function(width){
14259             ml.setWidth(width);
14260         },
14261
14262         /**
14263          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14264          * Returns the measured width of the specified text
14265          * @param {String} text The text to measure
14266          * @return {Number} width The width in pixels
14267          */
14268         getWidth : function(text){
14269             ml.dom.style.width = 'auto';
14270             return this.getSize(text).width;
14271         },
14272
14273         /**
14274          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14275          * Returns the measured height of the specified text.  For multiline text, be sure to call
14276          * {@link #setFixedWidth} if necessary.
14277          * @param {String} text The text to measure
14278          * @return {Number} height The height in pixels
14279          */
14280         getHeight : function(text){
14281             return this.getSize(text).height;
14282         }
14283     };
14284
14285     instance.bind(bindTo);
14286
14287     return instance;
14288 };
14289
14290 Ext.Element.addMethods({
14291     /**
14292      * Returns the width in pixels of the passed text, or the width of the text in this Element.
14293      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14294      * @param {Number} min (Optional) The minumum value to return.
14295      * @param {Number} max (Optional) The maximum value to return.
14296      * @return {Number} The text width in pixels.
14297      * @member Ext.Element getTextWidth
14298      */
14299     getTextWidth : function(text, min, max){
14300         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14301     }
14302 });
14303 /**
14304  * @class Ext.util.Cookies
14305  * Utility class for managing and interacting with cookies.
14306  * @singleton
14307  */
14308 Ext.util.Cookies = {
14309     /**
14310      * Create a cookie with the specified name and value. Additional settings
14311      * for the cookie may be optionally specified (for example: expiration,
14312      * access restriction, SSL).
14313      * @param {String} name The name of the cookie to set. 
14314      * @param {Mixed} value The value to set for the cookie.
14315      * @param {Object} expires (Optional) Specify an expiration date the
14316      * cookie is to persist until.  Note that the specified Date object will
14317      * be converted to Greenwich Mean Time (GMT). 
14318      * @param {String} path (Optional) Setting a path on the cookie restricts
14319      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
14320      * @param {String} domain (Optional) Setting a domain restricts access to
14321      * pages on a given domain (typically used to allow cookie access across
14322      * subdomains). For example, "extjs.com" will create a cookie that can be
14323      * accessed from any subdomain of extjs.com, including www.extjs.com,
14324      * support.extjs.com, etc.
14325      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14326      * should only be accessible via SSL on a page using the HTTPS protocol.
14327      * Defaults to <tt>false</tt>. Note that this will only work if the page
14328      * calling this code uses the HTTPS protocol, otherwise the cookie will be
14329      * created with default options.
14330      */
14331     set : function(name, value){
14332         var argv = arguments;
14333         var argc = arguments.length;
14334         var expires = (argc > 2) ? argv[2] : null;
14335         var path = (argc > 3) ? argv[3] : '/';
14336         var domain = (argc > 4) ? argv[4] : null;
14337         var secure = (argc > 5) ? argv[5] : false;
14338         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14339     },
14340
14341     /**
14342      * Retrieves cookies that are accessible by the current page. If a cookie
14343      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
14344      * example retrieves the cookie called "valid" and stores the String value
14345      * in the variable <tt>validStatus</tt>.
14346      * <pre><code>
14347      * var validStatus = Ext.util.Cookies.get("valid");
14348      * </code></pre>
14349      * @param {String} name The name of the cookie to get
14350      * @return {Mixed} Returns the cookie value for the specified name;
14351      * null if the cookie name does not exist.
14352      */
14353     get : function(name){
14354         var arg = name + "=";
14355         var alen = arg.length;
14356         var clen = document.cookie.length;
14357         var i = 0;
14358         var j = 0;
14359         while(i < clen){
14360             j = i + alen;
14361             if(document.cookie.substring(i, j) == arg){
14362                 return Ext.util.Cookies.getCookieVal(j);
14363             }
14364             i = document.cookie.indexOf(" ", i) + 1;
14365             if(i === 0){
14366                 break;
14367             }
14368         }
14369         return null;
14370     },
14371
14372     /**
14373      * Removes a cookie with the provided name from the browser
14374      * if found by setting its expiration date to sometime in the past. 
14375      * @param {String} name The name of the cookie to remove
14376      */
14377     clear : function(name){
14378         if(Ext.util.Cookies.get(name)){
14379             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14380         }
14381     },
14382     /**
14383      * @private
14384      */
14385     getCookieVal : function(offset){
14386         var endstr = document.cookie.indexOf(";", offset);
14387         if(endstr == -1){
14388             endstr = document.cookie.length;
14389         }
14390         return unescape(document.cookie.substring(offset, endstr));
14391     }
14392 };/**
14393  * Framework-wide error-handler.  Developers can override this method to provide
14394  * custom exception-handling.  Framework errors will often extend from the base
14395  * Ext.Error class.
14396  * @param {Object/Error} e The thrown exception object.
14397  */
14398 Ext.handleError = function(e) {
14399     throw e;
14400 };
14401
14402 /**
14403  * @class Ext.Error
14404  * @extends Error
14405  * <p>A base error class. Future implementations are intended to provide more
14406  * robust error handling throughout the framework (<b>in the debug build only</b>)
14407  * to check for common errors and problems. The messages issued by this class
14408  * will aid error checking. Error checks will be automatically removed in the
14409  * production build so that performance is not negatively impacted.</p>
14410  * <p>Some sample messages currently implemented:</p><pre>
14411 "DataProxy attempted to execute an API-action but found an undefined
14412 url / function. Please review your Proxy url/api-configuration."
14413  * </pre><pre>
14414 "Could not locate your "root" property in your server response.
14415 Please review your JsonReader config to ensure the config-property
14416 "root" matches the property your server-response.  See the JsonReader
14417 docs for additional assistance."
14418  * </pre>
14419  * <p>An example of the code used for generating error messages:</p><pre><code>
14420 try {
14421     generateError({
14422         foo: 'bar'
14423     });
14424 }
14425 catch (e) {
14426     console.error(e);
14427 }
14428 function generateError(data) {
14429     throw new Ext.Error('foo-error', data);
14430 }
14431  * </code></pre>
14432  * @param {String} message
14433  */
14434 Ext.Error = function(message) {
14435     // Try to read the message from Ext.Error.lang
14436     this.message = (this.lang[message]) ? this.lang[message] : message;
14437 };
14438
14439 Ext.Error.prototype = new Error();
14440 Ext.apply(Ext.Error.prototype, {
14441     // protected.  Extensions place their error-strings here.
14442     lang: {},
14443
14444     name: 'Ext.Error',
14445     /**
14446      * getName
14447      * @return {String}
14448      */
14449     getName : function() {
14450         return this.name;
14451     },
14452     /**
14453      * getMessage
14454      * @return {String}
14455      */
14456     getMessage : function() {
14457         return this.message;
14458     },
14459     /**
14460      * toJson
14461      * @return {String}
14462      */
14463     toJson : function() {
14464         return Ext.encode(this);
14465     }
14466 });
14467 /**
14468  * @class Ext.ComponentMgr
14469  * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
14470  * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
14471  * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
14472  * <p>This object also provides a registry of available Component <i>classes</i>
14473  * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
14474  * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
14475  * when creating a full, nested config object for a complete Ext page.</p>
14476  * <p>A child Component may be specified simply as a <i>config object</i>
14477  * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
14478  * needs rendering, the correct type can be looked up for lazy instantiation.</p>
14479  * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
14480  * @singleton
14481  */
14482 Ext.ComponentMgr = function(){
14483     var all = new Ext.util.MixedCollection();
14484     var types = {};
14485     var ptypes = {};
14486
14487     return {
14488         /**
14489          * Registers a component.
14490          * @param {Ext.Component} c The component
14491          */
14492         register : function(c){
14493             all.add(c);
14494         },
14495
14496         /**
14497          * Unregisters a component.
14498          * @param {Ext.Component} c The component
14499          */
14500         unregister : function(c){
14501             all.remove(c);
14502         },
14503
14504         /**
14505          * Returns a component by {@link Ext.Component#id id}.
14506          * For additional details see {@link Ext.util.MixedCollection#get}.
14507          * @param {String} id The component {@link Ext.Component#id id}
14508          * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
14509          * Class was found.
14510          */
14511         get : function(id){
14512             return all.get(id);
14513         },
14514
14515         /**
14516          * Registers a function that will be called when a Component with the specified id is added to ComponentMgr. This will happen on instantiation.
14517          * @param {String} id The component {@link Ext.Component#id id}
14518          * @param {Function} fn The callback function
14519          * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
14520          */
14521         onAvailable : function(id, fn, scope){
14522             all.on("add", function(index, o){
14523                 if(o.id == id){
14524                     fn.call(scope || o, o);
14525                     all.un("add", fn, scope);
14526                 }
14527             });
14528         },
14529
14530         /**
14531          * The MixedCollection used internally for the component cache. An example usage may be subscribing to
14532          * events on the MixedCollection to monitor addition or removal.  Read-only.
14533          * @type {MixedCollection}
14534          */
14535         all : all,
14536         
14537         /**
14538          * The xtypes that have been registered with the component manager.
14539          * @type {Object}
14540          */
14541         types : types,
14542         
14543         /**
14544          * The ptypes that have been registered with the component manager.
14545          * @type {Object}
14546          */
14547         ptypes: ptypes,
14548         
14549         /**
14550          * Checks if a Component type is registered.
14551          * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
14552          * @return {Boolean} Whether the type is registered.
14553          */
14554         isRegistered : function(xtype){
14555             return types[xtype] !== undefined;    
14556         },
14557         
14558         /**
14559          * Checks if a Plugin type is registered.
14560          * @param {Ext.Component} ptype The mnemonic string by which the Plugin class may be looked up
14561          * @return {Boolean} Whether the type is registered.
14562          */
14563         isPluginRegistered : function(ptype){
14564             return ptypes[ptype] !== undefined;    
14565         },        
14566
14567         /**
14568          * <p>Registers a new Component constructor, keyed by a new
14569          * {@link Ext.Component#xtype}.</p>
14570          * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
14571          * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
14572          * child Components.
14573          * see {@link Ext.Container#items}</p>
14574          * @param {String} xtype The mnemonic string by which the Component class may be looked up.
14575          * @param {Constructor} cls The new Component class.
14576          */
14577         registerType : function(xtype, cls){
14578             types[xtype] = cls;
14579             cls.xtype = xtype;
14580         },
14581
14582         /**
14583          * Creates a new Component from the specified config object using the
14584          * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
14585          * @param {Object} config A configuration object for the Component you wish to create.
14586          * @param {Constructor} defaultType The constructor to provide the default Component type if
14587          * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
14588          * @return {Ext.Component} The newly instantiated Component.
14589          */
14590         create : function(config, defaultType){
14591             return config.render ? config : new types[config.xtype || defaultType](config);
14592         },
14593
14594         /**
14595          * <p>Registers a new Plugin constructor, keyed by a new
14596          * {@link Ext.Component#ptype}.</p>
14597          * <p>Use this method (or its alias {@link Ext#preg Ext.preg}) to register new
14598          * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying
14599          * Plugins.</p>
14600          * @param {String} ptype The mnemonic string by which the Plugin class may be looked up.
14601          * @param {Constructor} cls The new Plugin class.
14602          */
14603         registerPlugin : function(ptype, cls){
14604             ptypes[ptype] = cls;
14605             cls.ptype = ptype;
14606         },
14607
14608         /**
14609          * Creates a new Plugin from the specified config object using the
14610          * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
14611          * @param {Object} config A configuration object for the Plugin you wish to create.
14612          * @param {Constructor} defaultType The constructor to provide the default Plugin type if
14613          * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
14614          * @return {Ext.Component} The newly instantiated Plugin.
14615          */
14616         createPlugin : function(config, defaultType){
14617             var PluginCls = ptypes[config.ptype || defaultType];
14618             if (PluginCls.init) {
14619                 return PluginCls;                
14620             } else {
14621                 return new PluginCls(config);
14622             }            
14623         }
14624     };
14625 }();
14626
14627 /**
14628  * Shorthand for {@link Ext.ComponentMgr#registerType}
14629  * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
14630  * may be looked up.
14631  * @param {Constructor} cls The new Component class.
14632  * @member Ext
14633  * @method reg
14634  */
14635 Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down
14636 /**
14637  * Shorthand for {@link Ext.ComponentMgr#registerPlugin}
14638  * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
14639  * may be looked up.
14640  * @param {Constructor} cls The new Plugin class.
14641  * @member Ext
14642  * @method preg
14643  */
14644 Ext.preg = Ext.ComponentMgr.registerPlugin;
14645 /**
14646  * Shorthand for {@link Ext.ComponentMgr#create}
14647  * Creates a new Component from the specified config object using the
14648  * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
14649  * @param {Object} config A configuration object for the Component you wish to create.
14650  * @param {Constructor} defaultType The constructor to provide the default Component type if
14651  * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
14652  * @return {Ext.Component} The newly instantiated Component.
14653  * @member Ext
14654  * @method create
14655  */
14656 Ext.create = Ext.ComponentMgr.create;/**
14657  * @class Ext.Component
14658  * @extends Ext.util.Observable
14659  * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
14660  * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
14661  * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
14662  * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
14663  * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
14664  * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
14665  * {@link Ext#getCmp}, passing the {@link #id}.</p>
14666  * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
14667  * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
14668  * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
14669  * and to either extend or augment ExtJs base classes to create custom Components.</p>
14670  * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
14671  * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
14672  * <pre>
14673 xtype            Class
14674 -------------    ------------------
14675 box              {@link Ext.BoxComponent}
14676 button           {@link Ext.Button}
14677 buttongroup      {@link Ext.ButtonGroup}
14678 colorpalette     {@link Ext.ColorPalette}
14679 component        {@link Ext.Component}
14680 container        {@link Ext.Container}
14681 cycle            {@link Ext.CycleButton}
14682 dataview         {@link Ext.DataView}
14683 datepicker       {@link Ext.DatePicker}
14684 editor           {@link Ext.Editor}
14685 editorgrid       {@link Ext.grid.EditorGridPanel}
14686 flash            {@link Ext.FlashComponent}
14687 grid             {@link Ext.grid.GridPanel}
14688 listview         {@link Ext.ListView}
14689 panel            {@link Ext.Panel}
14690 progress         {@link Ext.ProgressBar}
14691 propertygrid     {@link Ext.grid.PropertyGrid}
14692 slider           {@link Ext.Slider}
14693 spacer           {@link Ext.Spacer}
14694 splitbutton      {@link Ext.SplitButton}
14695 tabpanel         {@link Ext.TabPanel}
14696 treepanel        {@link Ext.tree.TreePanel}
14697 viewport         {@link Ext.ViewPort}
14698 window           {@link Ext.Window}
14699
14700 Toolbar components
14701 ---------------------------------------
14702 paging           {@link Ext.PagingToolbar}
14703 toolbar          {@link Ext.Toolbar}
14704 tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
14705 tbfill           {@link Ext.Toolbar.Fill}
14706 tbitem           {@link Ext.Toolbar.Item}
14707 tbseparator      {@link Ext.Toolbar.Separator}
14708 tbspacer         {@link Ext.Toolbar.Spacer}
14709 tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
14710 tbtext           {@link Ext.Toolbar.TextItem}
14711
14712 Menu components
14713 ---------------------------------------
14714 menu             {@link Ext.menu.Menu}
14715 colormenu        {@link Ext.menu.ColorMenu}
14716 datemenu         {@link Ext.menu.DateMenu}
14717 menubaseitem     {@link Ext.menu.BaseItem}
14718 menucheckitem    {@link Ext.menu.CheckItem}
14719 menuitem         {@link Ext.menu.Item}
14720 menuseparator    {@link Ext.menu.Separator}
14721 menutextitem     {@link Ext.menu.TextItem}
14722
14723 Form components
14724 ---------------------------------------
14725 form             {@link Ext.form.FormPanel}
14726 checkbox         {@link Ext.form.Checkbox}
14727 checkboxgroup    {@link Ext.form.CheckboxGroup}
14728 combo            {@link Ext.form.ComboBox}
14729 datefield        {@link Ext.form.DateField}
14730 displayfield     {@link Ext.form.DisplayField}
14731 field            {@link Ext.form.Field}
14732 fieldset         {@link Ext.form.FieldSet}
14733 hidden           {@link Ext.form.Hidden}
14734 htmleditor       {@link Ext.form.HtmlEditor}
14735 label            {@link Ext.form.Label}
14736 numberfield      {@link Ext.form.NumberField}
14737 radio            {@link Ext.form.Radio}
14738 radiogroup       {@link Ext.form.RadioGroup}
14739 textarea         {@link Ext.form.TextArea}
14740 textfield        {@link Ext.form.TextField}
14741 timefield        {@link Ext.form.TimeField}
14742 trigger          {@link Ext.form.TriggerField}
14743
14744 Chart components
14745 ---------------------------------------
14746 chart            {@link Ext.chart.Chart}
14747 barchart         {@link Ext.chart.BarChart}
14748 cartesianchart   {@link Ext.chart.CartesianChart}
14749 columnchart      {@link Ext.chart.ColumnChart}
14750 linechart        {@link Ext.chart.LineChart}
14751 piechart         {@link Ext.chart.PieChart}
14752
14753 Store xtypes
14754 ---------------------------------------
14755 arraystore       {@link Ext.data.ArrayStore}
14756 directstore      {@link Ext.data.DirectStore}
14757 groupingstore    {@link Ext.data.GroupingStore}
14758 jsonstore        {@link Ext.data.JsonStore}
14759 simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
14760 store            {@link Ext.data.Store}
14761 xmlstore         {@link Ext.data.XmlStore}
14762 </pre>
14763  * @constructor
14764  * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
14765  * <div class="mdetail-params"><ul>
14766  * <li><b>an element</b> :
14767  * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
14768  * <li><b>a string</b> :
14769  * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
14770  * <li><b>anything else</b> :
14771  * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
14772  * </ul></div>
14773  */
14774 Ext.Component = function(config){
14775     config = config || {};
14776     if(config.initialConfig){
14777         if(config.isAction){           // actions
14778             this.baseAction = config;
14779         }
14780         config = config.initialConfig; // component cloning / action set up
14781     }else if(config.tagName || config.dom || Ext.isString(config)){ // element object
14782         config = {applyTo: config, id: config.id || config};
14783     }
14784
14785     /**
14786      * This Component's initial configuration specification. Read-only.
14787      * @type Object
14788      * @property initialConfig
14789      */
14790     this.initialConfig = config;
14791
14792     Ext.apply(this, config);
14793     this.addEvents(
14794         /**
14795          * @event added
14796          * Fires when a component is added to an Ext.Container
14797          * @param {Ext.Component} this
14798          * @param {Ext.Container} ownerCt Container which holds the component
14799          * @param {number} index Position at which the component was added
14800          */
14801         'added',
14802         /**
14803          * @event disable
14804          * Fires after the component is disabled.
14805          * @param {Ext.Component} this
14806          */
14807         'disable',
14808         /**
14809          * @event enable
14810          * Fires after the component is enabled.
14811          * @param {Ext.Component} this
14812          */
14813         'enable',
14814         /**
14815          * @event beforeshow
14816          * Fires before the component is shown by calling the {@link #show} method.
14817          * Return false from an event handler to stop the show.
14818          * @param {Ext.Component} this
14819          */
14820         'beforeshow',
14821         /**
14822          * @event show
14823          * Fires after the component is shown when calling the {@link #show} method.
14824          * @param {Ext.Component} this
14825          */
14826         'show',
14827         /**
14828          * @event beforehide
14829          * Fires before the component is hidden by calling the {@link #hide} method.
14830          * Return false from an event handler to stop the hide.
14831          * @param {Ext.Component} this
14832          */
14833         'beforehide',
14834         /**
14835          * @event hide
14836          * Fires after the component is hidden.
14837          * Fires after the component is hidden when calling the {@link #hide} method.
14838          * @param {Ext.Component} this
14839          */
14840         'hide',
14841         /**
14842          * @event removed
14843          * Fires when a component is removed from an Ext.Container
14844          * @param {Ext.Component} this
14845          * @param {Ext.Container} ownerCt Container which holds the component
14846          */
14847         'removed',
14848         /**
14849          * @event beforerender
14850          * Fires before the component is {@link #rendered}. Return false from an
14851          * event handler to stop the {@link #render}.
14852          * @param {Ext.Component} this
14853          */
14854         'beforerender',
14855         /**
14856          * @event render
14857          * Fires after the component markup is {@link #rendered}.
14858          * @param {Ext.Component} this
14859          */
14860         'render',
14861         /**
14862          * @event afterrender
14863          * <p>Fires after the component rendering is finished.</p>
14864          * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
14865          * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
14866          * has been restored.</p>
14867          * @param {Ext.Component} this
14868          */
14869         'afterrender',
14870         /**
14871          * @event beforedestroy
14872          * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
14873          * @param {Ext.Component} this
14874          */
14875         'beforedestroy',
14876         /**
14877          * @event destroy
14878          * Fires after the component is {@link #destroy}ed.
14879          * @param {Ext.Component} this
14880          */
14881         'destroy',
14882         /**
14883          * @event beforestaterestore
14884          * Fires before the state of the component is restored. Return false from an event handler to stop the restore.
14885          * @param {Ext.Component} this
14886          * @param {Object} state The hash of state values returned from the StateProvider. If this
14887          * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
14888          * that simply copies property values into this Component. The method maybe overriden to
14889          * provide custom state restoration.
14890          */
14891         'beforestaterestore',
14892         /**
14893          * @event staterestore
14894          * Fires after the state of the component is restored.
14895          * @param {Ext.Component} this
14896          * @param {Object} state The hash of state values returned from the StateProvider. This is passed
14897          * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
14898          * Component. The method maybe overriden to provide custom state restoration.
14899          */
14900         'staterestore',
14901         /**
14902          * @event beforestatesave
14903          * Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
14904          * @param {Ext.Component} this
14905          * @param {Object} state The hash of state values. This is determined by calling
14906          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14907          * developer to return whetever representation of state is required, by default, Ext.Component
14908          * has a null implementation.
14909          */
14910         'beforestatesave',
14911         /**
14912          * @event statesave
14913          * Fires after the state of the component is saved to the configured state provider.
14914          * @param {Ext.Component} this
14915          * @param {Object} state The hash of state values. This is determined by calling
14916          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14917          * developer to return whetever representation of state is required, by default, Ext.Component
14918          * has a null implementation.
14919          */
14920         'statesave'
14921     );
14922     this.getId();
14923     Ext.ComponentMgr.register(this);
14924     Ext.Component.superclass.constructor.call(this);
14925
14926     if(this.baseAction){
14927         this.baseAction.addComponent(this);
14928     }
14929
14930     this.initComponent();
14931
14932     if(this.plugins){
14933         if(Ext.isArray(this.plugins)){
14934             for(var i = 0, len = this.plugins.length; i < len; i++){
14935                 this.plugins[i] = this.initPlugin(this.plugins[i]);
14936             }
14937         }else{
14938             this.plugins = this.initPlugin(this.plugins);
14939         }
14940     }
14941
14942     if(this.stateful !== false){
14943         this.initState();
14944     }
14945
14946     if(this.applyTo){
14947         this.applyToMarkup(this.applyTo);
14948         delete this.applyTo;
14949     }else if(this.renderTo){
14950         this.render(this.renderTo);
14951         delete this.renderTo;
14952     }
14953 };
14954
14955 // private
14956 Ext.Component.AUTO_ID = 1000;
14957
14958 Ext.extend(Ext.Component, Ext.util.Observable, {
14959     // Configs below are used for all Components when rendered by FormLayout.
14960     /**
14961      * @cfg {String} fieldLabel <p>The label text to display next to this Component (defaults to '').</p>
14962      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container which
14963      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
14964      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
14965      * <p>Also see <tt>{@link #hideLabel}</tt> and
14966      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
14967      * Example use:<pre><code>
14968 new Ext.FormPanel({
14969     height: 100,
14970     renderTo: Ext.getBody(),
14971     items: [{
14972         xtype: 'textfield',
14973         fieldLabel: 'Name'
14974     }]
14975 });
14976 </code></pre>
14977      */
14978     /**
14979      * @cfg {String} labelStyle <p>A CSS style specification string to apply directly to this field's
14980      * label.  Defaults to the container's labelStyle value if set (e.g.,
14981      * <tt>{@link Ext.layout.FormLayout#labelStyle}</tt> , or '').</p>
14982      * <br><p><b>Note</b>: see the note for <code>{@link #clearCls}</code>.</p><br>
14983      * <p>Also see <code>{@link #hideLabel}</code> and
14984      * <code>{@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</code></p>
14985      * Example use:<pre><code>
14986 new Ext.FormPanel({
14987     height: 100,
14988     renderTo: Ext.getBody(),
14989     items: [{
14990         xtype: 'textfield',
14991         fieldLabel: 'Name',
14992         labelStyle: 'font-weight:bold;'
14993     }]
14994 });
14995 </code></pre>
14996      */
14997     /**
14998      * @cfg {String} labelSeparator <p>The separator to display after the text of each
14999      * <tt>{@link #fieldLabel}</tt>.  This property may be configured at various levels.
15000      * The order of precedence is:
15001      * <div class="mdetail-params"><ul>
15002      * <li>field / component level</li>
15003      * <li>container level</li>
15004      * <li>{@link Ext.layout.FormLayout#labelSeparator layout level} (defaults to colon <tt>':'</tt>)</li>
15005      * </ul></div>
15006      * To display no separator for this field's label specify empty string ''.</p>
15007      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
15008      * <p>Also see <tt>{@link #hideLabel}</tt> and
15009      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
15010      * Example use:<pre><code>
15011 new Ext.FormPanel({
15012     height: 100,
15013     renderTo: Ext.getBody(),
15014     layoutConfig: {
15015         labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
15016     },
15017     {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
15018     items: [{
15019         xtype: 'textfield',
15020         fieldLabel: 'Field 1',
15021         labelSeparator: '...' // field/component level config supersedes others
15022     },{
15023         xtype: 'textfield',
15024         fieldLabel: 'Field 2' // labelSeparator will be '='
15025     }]
15026 });
15027 </code></pre>
15028      */
15029     /**
15030      * @cfg {Boolean} hideLabel <p><tt>true</tt> to completely hide the label element
15031      * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to <tt>false</tt>.
15032      * By default, even if you do not specify a <tt>{@link #fieldLabel}</tt> the space will still be
15033      * reserved so that the field will line up with other fields that do have labels.
15034      * Setting this to <tt>true</tt> will cause the field to not reserve that space.</p>
15035      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
15036      * Example use:<pre><code>
15037 new Ext.FormPanel({
15038     height: 100,
15039     renderTo: Ext.getBody(),
15040     items: [{
15041         xtype: 'textfield'
15042         hideLabel: true
15043     }]
15044 });
15045 </code></pre>
15046      */
15047     /**
15048      * @cfg {String} clearCls <p>The CSS class used to to apply to the special clearing div rendered
15049      * directly after each form field wrapper to provide field clearing (defaults to
15050      * <tt>'x-form-clear-left'</tt>).</p>
15051      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container
15052      * which has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout
15053      * manager (e.g. {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) and either a
15054      * <tt>{@link #fieldLabel}</tt> is specified or <tt>isFormField=true</tt> is specified.</p><br>
15055      * <p>See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.</p>
15056      */
15057     /**
15058      * @cfg {String} itemCls
15059      * <p><b>Note</b>: this config is only used when this Component is rendered by a Container which
15060      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
15061      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
15062      * <p>An additional CSS class to apply to the div wrapping the form item
15063      * element of this field.  If supplied, <tt>itemCls</tt> at the <b>field</b> level will override
15064      * the default <tt>itemCls</tt> supplied at the <b>container</b> level. The value specified for
15065      * <tt>itemCls</tt> will be added to the default class (<tt>'x-form-item'</tt>).</p>
15066      * <p>Since it is applied to the item wrapper (see
15067      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows
15068      * you to write standard CSS rules that can apply to the field, the label (if specified), or
15069      * any other element within the markup for the field.</p>
15070      * <br><p><b>Note</b>: see the note for <tt>{@link #fieldLabel}</tt>.</p><br>
15071      * Example use:<pre><code>
15072 // Apply a style to the field&#39;s label:
15073 &lt;style>
15074     .required .x-form-item-label {font-weight:bold;color:red;}
15075 &lt;/style>
15076
15077 new Ext.FormPanel({
15078     height: 100,
15079     renderTo: Ext.getBody(),
15080     items: [{
15081         xtype: 'textfield',
15082         fieldLabel: 'Name',
15083         itemCls: 'required' //this label will be styled
15084     },{
15085         xtype: 'textfield',
15086         fieldLabel: 'Favorite Color'
15087     }]
15088 });
15089 </code></pre>
15090      */
15091
15092     /**
15093      * @cfg {String} id
15094      * <p>The <b>unique</b> id of this component (defaults to an {@link #getId auto-assigned id}).
15095      * You should assign an id if you need to be able to access the component later and you do
15096      * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).</p>
15097      * <p>Note that this id will also be used as the element id for the containing HTML element
15098      * that is rendered to the page for this component. This allows you to write id-based CSS
15099      * rules to style the specific instance of this component uniquely, and also to select
15100      * sub-elements using this component's id as the parent.</p>
15101      * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see
15102      * <code>{@link #itemId}</code> and <code>{@link #ref}</code>.</p>
15103      * <p><b>Note</b>: to access the container of an item see <code>{@link #ownerCt}</code>.</p>
15104      */
15105     /**
15106      * @cfg {String} itemId
15107      * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
15108      * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
15109      * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
15110      * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve
15111      * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
15112      * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
15113      * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a <b>unique</b>
15114      * <code>{@link #id}</code>.</p>
15115      * <pre><code>
15116 var c = new Ext.Panel({ //
15117     {@link Ext.BoxComponent#height height}: 300,
15118     {@link #renderTo}: document.body,
15119     {@link Ext.Container#layout layout}: 'auto',
15120     {@link Ext.Container#items items}: [
15121         {
15122             itemId: 'p1',
15123             {@link Ext.Panel#title title}: 'Panel 1',
15124             {@link Ext.BoxComponent#height height}: 150
15125         },
15126         {
15127             itemId: 'p2',
15128             {@link Ext.Panel#title title}: 'Panel 2',
15129             {@link Ext.BoxComponent#height height}: 150
15130         }
15131     ]
15132 })
15133 p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
15134 p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
15135      * </code></pre>
15136      * <p>Also see <tt>{@link #id}</tt> and <code>{@link #ref}</code>.</p>
15137      * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
15138      */
15139     /**
15140      * @cfg {String} xtype
15141      * The registered <tt>xtype</tt> to create. This config option is not used when passing
15142      * a config object into a constructor. This config option is used only when
15143      * lazy instantiation is being used, and a child item of a Container is being
15144      * specified not as a fully instantiated Component, but as a <i>Component config
15145      * object</i>. The <tt>xtype</tt> will be looked up at render time up to determine what
15146      * type of child Component to create.<br><br>
15147      * The predefined xtypes are listed {@link Ext.Component here}.
15148      * <br><br>
15149      * If you subclass Components to create your own Components, you may register
15150      * them using {@link Ext.ComponentMgr#registerType} in order to be able to
15151      * take advantage of lazy instantiation and rendering.
15152      */
15153     /**
15154      * @cfg {String} ptype
15155      * The registered <tt>ptype</tt> to create. This config option is not used when passing
15156      * a config object into a constructor. This config option is used only when
15157      * lazy instantiation is being used, and a Plugin is being
15158      * specified not as a fully instantiated Component, but as a <i>Component config
15159      * object</i>. The <tt>ptype</tt> will be looked up at render time up to determine what
15160      * type of Plugin to create.<br><br>
15161      * If you create your own Plugins, you may register them using
15162      * {@link Ext.ComponentMgr#registerPlugin} in order to be able to
15163      * take advantage of lazy instantiation and rendering.
15164      */
15165     /**
15166      * @cfg {String} cls
15167      * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
15168      * useful for adding customized styles to the component or any of its children using standard CSS rules.
15169      */
15170     /**
15171      * @cfg {String} overCls
15172      * An optional extra CSS class that will be added to this component's Element when the mouse moves
15173      * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
15174      * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
15175      */
15176     /**
15177      * @cfg {String} style
15178      * A custom style specification to be applied to this component's Element.  Should be a valid argument to
15179      * {@link Ext.Element#applyStyles}.
15180      * <pre><code>
15181 new Ext.Panel({
15182     title: 'Some Title',
15183     renderTo: Ext.getBody(),
15184     width: 400, height: 300,
15185     layout: 'form',
15186     items: [{
15187         xtype: 'textarea',
15188         style: {
15189             width: '95%',
15190             marginBottom: '10px'
15191         }
15192     },
15193         new Ext.Button({
15194             text: 'Send',
15195             minWidth: '100',
15196             style: {
15197                 marginBottom: '10px'
15198             }
15199         })
15200     ]
15201 });
15202      * </code></pre>
15203      */
15204     /**
15205      * @cfg {String} ctCls
15206      * <p>An optional extra CSS class that will be added to this component's container. This can be useful for
15207      * adding customized styles to the container or any of its children using standard CSS rules.  See
15208      * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.</p>
15209      * <p><b>Note</b>: <tt>ctCls</tt> defaults to <tt>''</tt> except for the following class
15210      * which assigns a value by default:
15211      * <div class="mdetail-params"><ul>
15212      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-layout-ct'</tt></li>
15213      * </ul></div>
15214      * To configure the above Class with an extra CSS class append to the default.  For example,
15215      * for BoxLayout (Hbox and Vbox):<pre><code>
15216      * ctCls: 'x-box-layout-ct custom-class'
15217      * </code></pre>
15218      * </p>
15219      */
15220     /**
15221      * @cfg {Boolean} disabled
15222      * Render this component disabled (default is false).
15223      */
15224     disabled : false,
15225     /**
15226      * @cfg {Boolean} hidden
15227      * Render this component hidden (default is false). If <tt>true</tt>, the
15228      * {@link #hide} method will be called internally.
15229      */
15230     hidden : false,
15231     /**
15232      * @cfg {Object/Array} plugins
15233      * An object or array of objects that will provide custom functionality for this component.  The only
15234      * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
15235      * When a component is created, if any plugins are available, the component will call the init method on each
15236      * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
15237      * component as needed to provide its functionality.
15238      */
15239     /**
15240      * @cfg {Mixed} applyTo
15241      * <p>Specify the id of the element, a DOM element or an existing Element corresponding to a DIV
15242      * that is already present in the document that specifies some structural markup for this
15243      * component.</p><div><ul>
15244      * <li><b>Description</b> : <ul>
15245      * <div class="sub-desc">When <tt>applyTo</tt> is used, constituent parts of the component can also be specified
15246      * by id or CSS class name within the main element, and the component being created may attempt
15247      * to create its subcomponents from that markup if applicable.</div>
15248      * </ul></li>
15249      * <li><b>Notes</b> : <ul>
15250      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
15251      * <div class="sub-desc">If applyTo is specified, any value passed for {@link #renderTo} will be ignored and the target
15252      * element's parent node will automatically be used as the component's container.</div>
15253      * </ul></li>
15254      * </ul></div>
15255      */
15256     /**
15257      * @cfg {Mixed} renderTo
15258      * <p>Specify the id of the element, a DOM element or an existing Element that this component
15259      * will be rendered into.</p><div><ul>
15260      * <li><b>Notes</b> : <ul>
15261      * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
15262      * a {@link Ext.Container Container}. It is the responsibility of the
15263      * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
15264      * to render and manage its child items.</div>
15265      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
15266      * </ul></li>
15267      * </ul></div>
15268      * <p>See <tt>{@link #render}</tt> also.</p>
15269      */
15270     /**
15271      * @cfg {Boolean} stateful
15272      * <p>A flag which causes the Component to attempt to restore the state of
15273      * internal properties from a saved state on startup. The component must have
15274      * either a <code>{@link #stateId}</code> or <code>{@link #id}</code> assigned
15275      * for state to be managed. Auto-generated ids are not guaranteed to be stable
15276      * across page loads and cannot be relied upon to save and restore the same
15277      * state for a component.<p>
15278      * <p>For state saving to work, the state manager's provider must have been
15279      * set to an implementation of {@link Ext.state.Provider} which overrides the
15280      * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
15281      * methods to save and recall name/value pairs. A built-in implementation,
15282      * {@link Ext.state.CookieProvider} is available.</p>
15283      * <p>To set the state provider for the current page:</p>
15284      * <pre><code>
15285 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
15286     expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
15287 }));
15288      * </code></pre>
15289      * <p>A stateful Component attempts to save state when one of the events
15290      * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
15291      * <p>To save state, a stateful Component first serializes its state by
15292      * calling <b><code>getState</code></b>. By default, this function does
15293      * nothing. The developer must provide an implementation which returns an
15294      * object hash which represents the Component's restorable state.</p>
15295      * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
15296      * which uses the configured {@link Ext.state.Provider} to save the object
15297      * keyed by the Component's <code>{@link stateId}</code>, or, if that is not
15298      * specified, its <code>{@link #id}</code>.</p>
15299      * <p>During construction, a stateful Component attempts to <i>restore</i>
15300      * its state by calling {@link Ext.state.Manager#get} passing the
15301      * <code>{@link #stateId}</code>, or, if that is not specified, the
15302      * <code>{@link #id}</code>.</p>
15303      * <p>The resulting object is passed to <b><code>applyState</code></b>.
15304      * The default implementation of <code>applyState</code> simply copies
15305      * properties into the object, but a developer may override this to support
15306      * more behaviour.</p>
15307      * <p>You can perform extra processing on state save and restore by attaching
15308      * handlers to the {@link #beforestaterestore}, {@link #staterestore},
15309      * {@link #beforestatesave} and {@link #statesave} events.</p>
15310      */
15311     /**
15312      * @cfg {String} stateId
15313      * The unique id for this component to use for state management purposes
15314      * (defaults to the component id if one was set, otherwise null if the
15315      * component is using a generated id).
15316      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
15317      * restoring Component state.</p>
15318      */
15319     /**
15320      * @cfg {Array} stateEvents
15321      * <p>An array of events that, when fired, should trigger this component to
15322      * save its state (defaults to none). <code>stateEvents</code> may be any type
15323      * of event supported by this component, including browser or custom events
15324      * (e.g., <tt>['click', 'customerchange']</tt>).</p>
15325      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
15326      * restoring Component state.</p>
15327      */
15328     /**
15329      * @cfg {Mixed} autoEl
15330      * <p>A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
15331      * encapsulate this Component.</p>
15332      * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent},
15333      * and {@link Ext.Container}, this defaults to <b><tt>'div'</tt></b>. The more complex Ext classes use a more complex
15334      * DOM structure created by their own onRender methods.</p>
15335      * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
15336      * different DOM elements. Example usage:</p><pre><code>
15337 {
15338     xtype: 'box',
15339     autoEl: {
15340         tag: 'img',
15341         src: 'http://www.example.com/example.jpg'
15342     }
15343 }, {
15344     xtype: 'box',
15345     autoEl: {
15346         tag: 'blockquote',
15347         html: 'autoEl is cool!'
15348     }
15349 }, {
15350     xtype: 'container',
15351     autoEl: 'ul',
15352     cls: 'ux-unordered-list',
15353     items: {
15354         xtype: 'box',
15355         autoEl: 'li',
15356         html: 'First list item'
15357     }
15358 }
15359 </code></pre>
15360      */
15361     autoEl : 'div',
15362
15363     /**
15364      * @cfg {String} disabledClass
15365      * CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
15366      */
15367     disabledClass : 'x-item-disabled',
15368     /**
15369      * @cfg {Boolean} allowDomMove
15370      * Whether the component can move the Dom node when rendering (defaults to true).
15371      */
15372     allowDomMove : true,
15373     /**
15374      * @cfg {Boolean} autoShow
15375      * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove
15376      * them on render (defaults to false).
15377      */
15378     autoShow : false,
15379     /**
15380      * @cfg {String} hideMode
15381      * <p>How this component should be hidden. Supported values are <tt>'visibility'</tt>
15382      * (css visibility), <tt>'offsets'</tt> (negative offset position) and <tt>'display'</tt>
15383      * (css display).</p>
15384      * <br><p><b>Note</b>: the default of <tt>'display'</tt> is generally preferred
15385      * since items are automatically laid out when they are first shown (no sizing
15386      * is done while hidden).</p>
15387      */
15388     hideMode : 'display',
15389     /**
15390      * @cfg {Boolean} hideParent
15391      * True to hide and show the component's container when hide/show is called on the component, false to hide
15392      * and show the component itself (defaults to false).  For example, this can be used as a shortcut for a hide
15393      * button on a window by setting hide:true on the button when adding it to its parent container.
15394      */
15395     hideParent : false,
15396     /**
15397      * <p>The {@link Ext.Element} which encapsulates this Component. Read-only.</p>
15398      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
15399      * that may be overridden using the <code>{@link #autoEl}</code> config.</p>
15400      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
15401      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
15402      * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners}
15403      * config for a suggestion, or use a render listener directly:</p><pre><code>
15404 new Ext.Panel({
15405     title: 'The Clickable Panel',
15406     listeners: {
15407         render: function(p) {
15408             // Append the Panel to the click handler&#39;s argument list.
15409             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
15410         },
15411         single: true  // Remove the listener after first invocation
15412     }
15413 });
15414 </code></pre>
15415      * <p>See also <tt>{@link #getEl getEl}</p>
15416      * @type Ext.Element
15417      * @property el
15418      */
15419     /**
15420      * This Component's owner {@link Ext.Container Container} (defaults to undefined, and is set automatically when
15421      * this Component is added to a Container).  Read-only.
15422      * <p><b>Note</b>: to access items within the Container see <tt>{@link #itemId}</tt>.</p>
15423      * @type Ext.Container
15424      * @property ownerCt
15425      */
15426     /**
15427      * True if this component is hidden. Read-only.
15428      * @type Boolean
15429      * @property hidden
15430      */
15431     /**
15432      * True if this component is disabled. Read-only.
15433      * @type Boolean
15434      * @property disabled
15435      */
15436     /**
15437      * True if this component has been rendered. Read-only.
15438      * @type Boolean
15439      * @property rendered
15440      */
15441     rendered : false,
15442
15443     /**
15444      * @cfg {String} contentEl
15445      * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as the content
15446      * for this component.</p>
15447      * <ul>
15448      * <li><b>Description</b> :
15449      * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the layout element
15450      * of a new component (it simply moves the specified DOM element <i>after the Component is rendered</i> to use as the content.</div></li>
15451      * <li><b>Notes</b> :
15452      * <div class="sub-desc">The specified HTML element is appended to the layout element of the component <i>after any configured
15453      * {@link #html HTML} has been inserted</i>, and so the document will not contain this element at the time the {@link #render} event is fired.</div>
15454      * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.Container#layout layout}</b></code>
15455      * scheme that the Component may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.Container#items items}</b></code>.</div>
15456      * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
15457      * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
15458      * </ul>
15459      */
15460     /**
15461      * @cfg {String/Object} html
15462      * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element
15463      * content (defaults to ''). The HTML content is added after the component is rendered,
15464      * so the document will not contain this HTML at the time the {@link #render} event is fired.
15465      * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
15466      */
15467
15468     /**
15469      * @cfg {Mixed} tpl
15470      * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
15471      * or an array of strings to form an Ext.XTemplate.
15472      * Used in conjunction with the <code>{@link #data}</code> and
15473      * <code>{@link #tplWriteMode}</code> configurations.
15474      */
15475
15476     /**
15477      * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
15478      * updating the content area of the Component. Defaults to <tt>'overwrite'</tt>
15479      * (see <code>{@link Ext.XTemplate#overwrite}</code>).
15480      */
15481     tplWriteMode : 'overwrite',
15482
15483     /**
15484      * @cfg {Mixed} data
15485      * The initial set of data to apply to the <code>{@link #tpl}</code> to
15486      * update the content area of the Component.
15487      */
15488     
15489     /**
15490      * @cfg {Array} bubbleEvents
15491      * <p>An array of events that, when fired, should be bubbled to any parent container.
15492      * See {@link Ext.util.Observable#enableBubble}.
15493      * Defaults to <tt>[]</tt>.
15494      */
15495     bubbleEvents: [],
15496
15497
15498     // private
15499     ctype : 'Ext.Component',
15500
15501     // private
15502     actionMode : 'el',
15503
15504     // private
15505     getActionEl : function(){
15506         return this[this.actionMode];
15507     },
15508
15509     initPlugin : function(p){
15510         if(p.ptype && !Ext.isFunction(p.init)){
15511             p = Ext.ComponentMgr.createPlugin(p);
15512         }else if(Ext.isString(p)){
15513             p = Ext.ComponentMgr.createPlugin({
15514                 ptype: p
15515             });
15516         }
15517         p.init(this);
15518         return p;
15519     },
15520
15521     /* // protected
15522      * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
15523      * <pre><code>
15524 // Traditional constructor:
15525 Ext.Foo = function(config){
15526     // call superclass constructor:
15527     Ext.Foo.superclass.constructor.call(this, config);
15528
15529     this.addEvents({
15530         // add events
15531     });
15532 };
15533 Ext.extend(Ext.Foo, Ext.Bar, {
15534    // class body
15535 }
15536
15537 // initComponent replaces the constructor:
15538 Ext.Foo = Ext.extend(Ext.Bar, {
15539     initComponent : function(){
15540         // call superclass initComponent
15541         Ext.Container.superclass.initComponent.call(this);
15542
15543         this.addEvents({
15544             // add events
15545         });
15546     }
15547 }
15548 </code></pre>
15549      */
15550     initComponent : function(){
15551         /*
15552          * this is double processing, however it allows people to be able to do
15553          * Ext.apply(this, {
15554          *     listeners: {
15555          *         //here
15556          *     }
15557          * });
15558          * MyClass.superclass.initComponent.call(this);
15559          */
15560         if(this.listeners){
15561             this.on(this.listeners);
15562             delete this.listeners;
15563         }
15564         this.enableBubble(this.bubbleEvents);
15565     },
15566
15567     /**
15568      * <p>Render this Component into the passed HTML element.</p>
15569      * <p><b>If you are using a {@link Ext.Container Container} object to house this Component, then
15570      * do not use the render method.</b></p>
15571      * <p>A Container's child Components are rendered by that Container's
15572      * {@link Ext.Container#layout layout} manager when the Container is first rendered.</p>
15573      * <p>Certain layout managers allow dynamic addition of child components. Those that do
15574      * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
15575      * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
15576      * <p>If the Container is already rendered when a new child Component is added, you may need to call
15577      * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any
15578      * unrendered child Components to be rendered. This is required so that you can add multiple
15579      * child components if needed while only refreshing the layout once.</p>
15580      * <p>When creating complex UIs, it is important to remember that sizing and positioning
15581      * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager.
15582      * If you expect child items to be sized in response to user interactions, you must
15583      * configure the Container with a layout manager which creates and manages the type of layout you
15584      * have in mind.</p>
15585      * <p><b>Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
15586      * layout manager is used which does nothing but render child components sequentially into the
15587      * Container. No sizing or positioning will be performed in this situation.</b></p>
15588      * @param {Element/HTMLElement/String} container (optional) The element this Component should be
15589      * rendered into. If it is being created from existing markup, this should be omitted.
15590      * @param {String/Number} position (optional) The element ID or DOM node index within the container <b>before</b>
15591      * which this component will be inserted (defaults to appending to the end of the container)
15592      */
15593     render : function(container, position){
15594         if(!this.rendered && this.fireEvent('beforerender', this) !== false){
15595             if(!container && this.el){
15596                 this.el = Ext.get(this.el);
15597                 container = this.el.dom.parentNode;
15598                 this.allowDomMove = false;
15599             }
15600             this.container = Ext.get(container);
15601             if(this.ctCls){
15602                 this.container.addClass(this.ctCls);
15603             }
15604             this.rendered = true;
15605             if(position !== undefined){
15606                 if(Ext.isNumber(position)){
15607                     position = this.container.dom.childNodes[position];
15608                 }else{
15609                     position = Ext.getDom(position);
15610                 }
15611             }
15612             this.onRender(this.container, position || null);
15613             if(this.autoShow){
15614                 this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
15615             }
15616             if(this.cls){
15617                 this.el.addClass(this.cls);
15618                 delete this.cls;
15619             }
15620             if(this.style){
15621                 this.el.applyStyles(this.style);
15622                 delete this.style;
15623             }
15624             if(this.overCls){
15625                 this.el.addClassOnOver(this.overCls);
15626             }
15627             this.fireEvent('render', this);
15628
15629
15630             // Populate content of the component with html, contentEl or
15631             // a tpl.
15632             var contentTarget = this.getContentTarget();
15633             if (this.html){
15634                 contentTarget.update(Ext.DomHelper.markup(this.html));
15635                 delete this.html;
15636             }
15637             if (this.contentEl){
15638                 var ce = Ext.getDom(this.contentEl);
15639                 Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
15640                 contentTarget.appendChild(ce);
15641             }
15642             if (this.tpl) {
15643                 if (!this.tpl.compile) {
15644                     this.tpl = new Ext.XTemplate(this.tpl);
15645                 }
15646                 if (this.data) {
15647                     this.tpl[this.tplWriteMode](contentTarget, this.data);
15648                     delete this.data;
15649                 }
15650             }
15651             this.afterRender(this.container);
15652
15653
15654             if(this.hidden){
15655                 // call this so we don't fire initial hide events.
15656                 this.doHide();
15657             }
15658             if(this.disabled){
15659                 // pass silent so the event doesn't fire the first time.
15660                 this.disable(true);
15661             }
15662
15663             if(this.stateful !== false){
15664                 this.initStateEvents();
15665             }
15666             this.fireEvent('afterrender', this);
15667         }
15668         return this;
15669     },
15670
15671
15672     /**
15673      * Update the content area of a component.
15674      * @param {Mixed} htmlOrData
15675      * If this component has been configured with a template via the tpl config
15676      * then it will use this argument as data to populate the template.
15677      * If this component was not configured with a template, the components
15678      * content area will be updated via Ext.Element update
15679      * @param {Boolean} loadScripts
15680      * (optional) Only legitimate when using the html configuration. Defaults to false
15681      * @param {Function} callback
15682      * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
15683      */
15684     update: function(htmlOrData, loadScripts, cb) {
15685         var contentTarget = this.getContentTarget();
15686         if (this.tpl && typeof htmlOrData !== "string") {
15687             this.tpl[this.tplWriteMode](contentTarget, htmlOrData || {});
15688         } else {
15689             var html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
15690             contentTarget.update(html, loadScripts, cb);
15691         }
15692     },
15693
15694
15695     /**
15696      * @private
15697      * Method to manage awareness of when components are added to their
15698      * respective Container, firing an added event.
15699      * References are established at add time rather than at render time.
15700      * @param {Ext.Container} container Container which holds the component
15701      * @param {number} pos Position at which the component was added
15702      */
15703     onAdded : function(container, pos) {
15704         this.ownerCt = container;
15705         this.initRef();
15706         this.fireEvent('added', this, container, pos);
15707     },
15708
15709     /**
15710      * @private
15711      * Method to manage awareness of when components are removed from their
15712      * respective Container, firing an removed event. References are properly
15713      * cleaned up after removing a component from its owning container.
15714      */
15715     onRemoved : function() {
15716         this.removeRef();
15717         this.fireEvent('removed', this, this.ownerCt);
15718         delete this.ownerCt;
15719     },
15720
15721     /**
15722      * @private
15723      * Method to establish a reference to a component.
15724      */
15725     initRef : function() {
15726         /**
15727          * @cfg {String} ref
15728          * <p>A path specification, relative to the Component's <code>{@link #ownerCt}</code>
15729          * specifying into which ancestor Container to place a named reference to this Component.</p>
15730          * <p>The ancestor axis can be traversed by using '/' characters in the path.
15731          * For example, to put a reference to a Toolbar Button into <i>the Panel which owns the Toolbar</i>:</p><pre><code>
15732 var myGrid = new Ext.grid.EditorGridPanel({
15733     title: 'My EditorGridPanel',
15734     store: myStore,
15735     colModel: myColModel,
15736     tbar: [{
15737         text: 'Save',
15738         handler: saveChanges,
15739         disabled: true,
15740         ref: '../saveButton'
15741     }],
15742     listeners: {
15743         afteredit: function() {
15744 //          The button reference is in the GridPanel
15745             myGrid.saveButton.enable();
15746         }
15747     }
15748 });
15749 </code></pre>
15750          * <p>In the code above, if the <code>ref</code> had been <code>'saveButton'</code>
15751          * the reference would have been placed into the Toolbar. Each '/' in the <code>ref</code>
15752          * moves up one level from the Component's <code>{@link #ownerCt}</code>.</p>
15753          * <p>Also see the <code>{@link #added}</code> and <code>{@link #removed}</code> events.</p>
15754          */
15755         if(this.ref && !this.refOwner){
15756             var levels = this.ref.split('/'),
15757                 last = levels.length,
15758                 i = 0,
15759                 t = this;
15760
15761             while(t && i < last){
15762                 t = t.ownerCt;
15763                 ++i;
15764             }
15765             if(t){
15766                 t[this.refName = levels[--i]] = this;
15767                 /**
15768                  * @type Ext.Container
15769                  * @property refOwner
15770                  * The ancestor Container into which the {@link #ref} reference was inserted if this Component
15771                  * is a child of a Container, and has been configured with a <code>ref</code>.
15772                  */
15773                 this.refOwner = t;
15774             }
15775         }
15776     },
15777
15778     removeRef : function() {
15779         if (this.refOwner && this.refName) {
15780             delete this.refOwner[this.refName];
15781             delete this.refOwner;
15782         }
15783     },
15784
15785     // private
15786     initState : function(){
15787         if(Ext.state.Manager){
15788             var id = this.getStateId();
15789             if(id){
15790                 var state = Ext.state.Manager.get(id);
15791                 if(state){
15792                     if(this.fireEvent('beforestaterestore', this, state) !== false){
15793                         this.applyState(Ext.apply({}, state));
15794                         this.fireEvent('staterestore', this, state);
15795                     }
15796                 }
15797             }
15798         }
15799     },
15800
15801     // private
15802     getStateId : function(){
15803         return this.stateId || ((/^(ext-comp-|ext-gen)/).test(String(this.id)) ? null : this.id);
15804     },
15805
15806     // private
15807     initStateEvents : function(){
15808         if(this.stateEvents){
15809             for(var i = 0, e; e = this.stateEvents[i]; i++){
15810                 this.on(e, this.saveState, this, {delay:100});
15811             }
15812         }
15813     },
15814
15815     // private
15816     applyState : function(state){
15817         if(state){
15818             Ext.apply(this, state);
15819         }
15820     },
15821
15822     // private
15823     getState : function(){
15824         return null;
15825     },
15826
15827     // private
15828     saveState : function(){
15829         if(Ext.state.Manager && this.stateful !== false){
15830             var id = this.getStateId();
15831             if(id){
15832                 var state = this.getState();
15833                 if(this.fireEvent('beforestatesave', this, state) !== false){
15834                     Ext.state.Manager.set(id, state);
15835                     this.fireEvent('statesave', this, state);
15836                 }
15837             }
15838         }
15839     },
15840
15841     /**
15842      * Apply this component to existing markup that is valid. With this function, no call to render() is required.
15843      * @param {String/HTMLElement} el
15844      */
15845     applyToMarkup : function(el){
15846         this.allowDomMove = false;
15847         this.el = Ext.get(el);
15848         this.render(this.el.dom.parentNode);
15849     },
15850
15851     /**
15852      * Adds a CSS class to the component's underlying element.
15853      * @param {string} cls The CSS class name to add
15854      * @return {Ext.Component} this
15855      */
15856     addClass : function(cls){
15857         if(this.el){
15858             this.el.addClass(cls);
15859         }else{
15860             this.cls = this.cls ? this.cls + ' ' + cls : cls;
15861         }
15862         return this;
15863     },
15864
15865     /**
15866      * Removes a CSS class from the component's underlying element.
15867      * @param {string} cls The CSS class name to remove
15868      * @return {Ext.Component} this
15869      */
15870     removeClass : function(cls){
15871         if(this.el){
15872             this.el.removeClass(cls);
15873         }else if(this.cls){
15874             this.cls = this.cls.split(' ').remove(cls).join(' ');
15875         }
15876         return this;
15877     },
15878
15879     // private
15880     // default function is not really useful
15881     onRender : function(ct, position){
15882         if(!this.el && this.autoEl){
15883             if(Ext.isString(this.autoEl)){
15884                 this.el = document.createElement(this.autoEl);
15885             }else{
15886                 var div = document.createElement('div');
15887                 Ext.DomHelper.overwrite(div, this.autoEl);
15888                 this.el = div.firstChild;
15889             }
15890             if (!this.el.id) {
15891                 this.el.id = this.getId();
15892             }
15893         }
15894         if(this.el){
15895             this.el = Ext.get(this.el);
15896             if(this.allowDomMove !== false){
15897                 ct.dom.insertBefore(this.el.dom, position);
15898                 if (div) {
15899                     Ext.removeNode(div);
15900                     div = null;
15901                 }
15902             }
15903         }
15904     },
15905
15906     // private
15907     getAutoCreate : function(){
15908         var cfg = Ext.isObject(this.autoCreate) ?
15909                       this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
15910         if(this.id && !cfg.id){
15911             cfg.id = this.id;
15912         }
15913         return cfg;
15914     },
15915
15916     // private
15917     afterRender : Ext.emptyFn,
15918
15919     /**
15920      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15921      * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from
15922      * {@link Ext.ComponentMgr}.  Destruction is generally handled automatically by the framework and this method
15923      * should usually not need to be called directly.
15924      *
15925      */
15926     destroy : function(){
15927         if(!this.isDestroyed){
15928             if(this.fireEvent('beforedestroy', this) !== false){
15929                 this.destroying = true;
15930                 this.beforeDestroy();
15931                 if(this.ownerCt && this.ownerCt.remove){
15932                     this.ownerCt.remove(this, false);
15933                 }
15934                 if(this.rendered){
15935                     this.el.remove();
15936                     if(this.actionMode == 'container' || this.removeMode == 'container'){
15937                         this.container.remove();
15938                     }
15939                 }
15940                 // Stop any buffered tasks
15941                 if(this.focusTask && this.focusTask.cancel){
15942                     this.focusTask.cancel();
15943                 }
15944                 this.onDestroy();
15945                 Ext.ComponentMgr.unregister(this);
15946                 this.fireEvent('destroy', this);
15947                 this.purgeListeners();
15948                 this.destroying = false;
15949                 this.isDestroyed = true;
15950             }
15951         }
15952     },
15953
15954     deleteMembers : function(){
15955         var args = arguments;
15956         for(var i = 0, len = args.length; i < len; ++i){
15957             delete this[args[i]];
15958         }
15959     },
15960
15961     // private
15962     beforeDestroy : Ext.emptyFn,
15963
15964     // private
15965     onDestroy  : Ext.emptyFn,
15966
15967     /**
15968      * <p>Returns the {@link Ext.Element} which encapsulates this Component.</p>
15969      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
15970      * that may be overridden using the {@link #autoEl} config.</p>
15971      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
15972      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
15973      * for this Component's own Observable events), see the {@link #listeners} config for a suggestion,
15974      * or use a render listener directly:</p><pre><code>
15975 new Ext.Panel({
15976     title: 'The Clickable Panel',
15977     listeners: {
15978         render: function(p) {
15979             // Append the Panel to the click handler&#39;s argument list.
15980             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
15981         },
15982         single: true  // Remove the listener after first invocation
15983     }
15984 });
15985 </code></pre>
15986      * @return {Ext.Element} The Element which encapsulates this Component.
15987      */
15988     getEl : function(){
15989         return this.el;
15990     },
15991
15992     // private
15993     getContentTarget : function(){
15994         return this.el;
15995     },
15996
15997     /**
15998      * Returns the <code>id</code> of this component or automatically generates and
15999      * returns an <code>id</code> if an <code>id</code> is not defined yet:<pre><code>
16000      * 'ext-comp-' + (++Ext.Component.AUTO_ID)
16001      * </code></pre>
16002      * @return {String} id
16003      */
16004     getId : function(){
16005         return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
16006     },
16007
16008     /**
16009      * Returns the <code>{@link #itemId}</code> of this component.  If an
16010      * <code>{@link #itemId}</code> was not assigned through configuration the
16011      * <code>id</code> is returned using <code>{@link #getId}</code>.
16012      * @return {String}
16013      */
16014     getItemId : function(){
16015         return this.itemId || this.getId();
16016     },
16017
16018     /**
16019      * Try to focus this component.
16020      * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
16021      * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
16022      * @return {Ext.Component} this
16023      */
16024     focus : function(selectText, delay){
16025         if(delay){
16026             this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]);
16027             this.focusTask.delay(Ext.isNumber(delay) ? delay : 10);
16028             return;
16029         }
16030         if(this.rendered && !this.isDestroyed){
16031             this.el.focus();
16032             if(selectText === true){
16033                 this.el.dom.select();
16034             }
16035         }
16036         return this;
16037     },
16038
16039     // private
16040     blur : function(){
16041         if(this.rendered){
16042             this.el.blur();
16043         }
16044         return this;
16045     },
16046
16047     /**
16048      * Disable this component and fire the 'disable' event.
16049      * @return {Ext.Component} this
16050      */
16051     disable : function(/* private */ silent){
16052         if(this.rendered){
16053             this.onDisable();
16054         }
16055         this.disabled = true;
16056         if(silent !== true){
16057             this.fireEvent('disable', this);
16058         }
16059         return this;
16060     },
16061
16062     // private
16063     onDisable : function(){
16064         this.getActionEl().addClass(this.disabledClass);
16065         this.el.dom.disabled = true;
16066     },
16067
16068     /**
16069      * Enable this component and fire the 'enable' event.
16070      * @return {Ext.Component} this
16071      */
16072     enable : function(){
16073         if(this.rendered){
16074             this.onEnable();
16075         }
16076         this.disabled = false;
16077         this.fireEvent('enable', this);
16078         return this;
16079     },
16080
16081     // private
16082     onEnable : function(){
16083         this.getActionEl().removeClass(this.disabledClass);
16084         this.el.dom.disabled = false;
16085     },
16086
16087     /**
16088      * Convenience function for setting disabled/enabled by boolean.
16089      * @param {Boolean} disabled
16090      * @return {Ext.Component} this
16091      */
16092     setDisabled : function(disabled){
16093         return this[disabled ? 'disable' : 'enable']();
16094     },
16095
16096     /**
16097      * Show this component.  Listen to the '{@link #beforeshow}' event and return
16098      * <tt>false</tt> to cancel showing the component.  Fires the '{@link #show}'
16099      * event after showing the component.
16100      * @return {Ext.Component} this
16101      */
16102     show : function(){
16103         if(this.fireEvent('beforeshow', this) !== false){
16104             this.hidden = false;
16105             if(this.autoRender){
16106                 this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
16107             }
16108             if(this.rendered){
16109                 this.onShow();
16110             }
16111             this.fireEvent('show', this);
16112         }
16113         return this;
16114     },
16115
16116     // private
16117     onShow : function(){
16118         this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
16119     },
16120
16121     /**
16122      * Hide this component.  Listen to the '{@link #beforehide}' event and return
16123      * <tt>false</tt> to cancel hiding the component.  Fires the '{@link #hide}'
16124      * event after hiding the component. Note this method is called internally if
16125      * the component is configured to be <code>{@link #hidden}</code>.
16126      * @return {Ext.Component} this
16127      */
16128     hide : function(){
16129         if(this.fireEvent('beforehide', this) !== false){
16130             this.doHide();
16131             this.fireEvent('hide', this);
16132         }
16133         return this;
16134     },
16135
16136     // private
16137     doHide: function(){
16138         this.hidden = true;
16139         if(this.rendered){
16140             this.onHide();
16141         }
16142     },
16143
16144     // private
16145     onHide : function(){
16146         this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
16147     },
16148
16149     // private
16150     getVisibilityEl : function(){
16151         return this.hideParent ? this.container : this.getActionEl();
16152     },
16153
16154     /**
16155      * Convenience function to hide or show this component by boolean.
16156      * @param {Boolean} visible True to show, false to hide
16157      * @return {Ext.Component} this
16158      */
16159     setVisible : function(visible){
16160         return this[visible ? 'show' : 'hide']();
16161     },
16162
16163     /**
16164      * Returns true if this component is visible.
16165      * @return {Boolean} True if this component is visible, false otherwise.
16166      */
16167     isVisible : function(){
16168         return this.rendered && this.getVisibilityEl().isVisible();
16169     },
16170
16171     /**
16172      * Clone the current component using the original config values passed into this instance by default.
16173      * @param {Object} overrides A new config containing any properties to override in the cloned version.
16174      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
16175      * @return {Ext.Component} clone The cloned copy of this component
16176      */
16177     cloneConfig : function(overrides){
16178         overrides = overrides || {};
16179         var id = overrides.id || Ext.id();
16180         var cfg = Ext.applyIf(overrides, this.initialConfig);
16181         cfg.id = id; // prevent dup id
16182         return new this.constructor(cfg);
16183     },
16184
16185     /**
16186      * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all
16187      * available xtypes, see the {@link Ext.Component} header. Example usage:
16188      * <pre><code>
16189 var t = new Ext.form.TextField();
16190 alert(t.getXType());  // alerts 'textfield'
16191 </code></pre>
16192      * @return {String} The xtype
16193      */
16194     getXType : function(){
16195         return this.constructor.xtype;
16196     },
16197
16198     /**
16199      * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
16200      * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
16201      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
16202      * to participate in determination of inherited xtypes.</b></p>
16203      * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
16204      * <p>Example usage:</p>
16205      * <pre><code>
16206 var t = new Ext.form.TextField();
16207 var isText = t.isXType('textfield');        // true
16208 var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
16209 var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
16210 </code></pre>
16211      * @param {String} xtype The xtype to check for this Component
16212      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
16213      * the default), or true to check whether this Component is directly of the specified xtype.
16214      * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
16215      */
16216     isXType : function(xtype, shallow){
16217         //assume a string by default
16218         if (Ext.isFunction(xtype)){
16219             xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
16220         }else if (Ext.isObject(xtype)){
16221             xtype = xtype.constructor.xtype; //handle being passed an instance
16222         }
16223
16224         return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
16225     },
16226
16227     /**
16228      * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
16229      * available xtypes, see the {@link Ext.Component} header.</p>
16230      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
16231      * to participate in determination of inherited xtypes.</b></p>
16232      * <p>Example usage:</p>
16233      * <pre><code>
16234 var t = new Ext.form.TextField();
16235 alert(t.getXTypes());  // alerts 'component/box/field/textfield'
16236 </code></pre>
16237      * @return {String} The xtype hierarchy string
16238      */
16239     getXTypes : function(){
16240         var tc = this.constructor;
16241         if(!tc.xtypes){
16242             var c = [], sc = this;
16243             while(sc && sc.constructor.xtype){
16244                 c.unshift(sc.constructor.xtype);
16245                 sc = sc.constructor.superclass;
16246             }
16247             tc.xtypeChain = c;
16248             tc.xtypes = c.join('/');
16249         }
16250         return tc.xtypes;
16251     },
16252
16253     /**
16254      * Find a container above this component at any level by a custom function. If the passed function returns
16255      * true, the container will be returned.
16256      * @param {Function} fn The custom function to call with the arguments (container, this component).
16257      * @return {Ext.Container} The first Container for which the custom function returns true
16258      */
16259     findParentBy : function(fn) {
16260         for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
16261         return p || null;
16262     },
16263
16264     /**
16265      * Find a container above this component at any level by xtype or class
16266      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
16267      * @return {Ext.Container} The first Container which matches the given xtype or class
16268      */
16269     findParentByType : function(xtype) {
16270         return Ext.isFunction(xtype) ?
16271             this.findParentBy(function(p){
16272                 return p.constructor === xtype;
16273             }) :
16274             this.findParentBy(function(p){
16275                 return p.constructor.xtype === xtype;
16276             });
16277     },
16278
16279     // protected
16280     getPositionEl : function(){
16281         return this.positionEl || this.el;
16282     },
16283
16284     // private
16285     purgeListeners : function(){
16286         Ext.Component.superclass.purgeListeners.call(this);
16287         if(this.mons){
16288             this.on('beforedestroy', this.clearMons, this, {single: true});
16289         }
16290     },
16291
16292     // private
16293     clearMons : function(){
16294         Ext.each(this.mons, function(m){
16295             m.item.un(m.ename, m.fn, m.scope);
16296         }, this);
16297         this.mons = [];
16298     },
16299
16300     // private
16301     createMons: function(){
16302         if(!this.mons){
16303             this.mons = [];
16304             this.on('beforedestroy', this.clearMons, this, {single: true});
16305         }
16306     },
16307
16308     /**
16309      * <p>Adds listeners to any Observable object (or Elements) which are automatically removed when this Component
16310      * is destroyed. Usage:</p><code><pre>
16311 myGridPanel.mon(myGridPanel.getSelectionModel(), 'selectionchange', handleSelectionChange, null, {buffer: 50});
16312 </pre></code>
16313      * <p>or:</p><code><pre>
16314 myGridPanel.mon(myGridPanel.getSelectionModel(), {
16315     selectionchange: handleSelectionChange,
16316     buffer: 50
16317 });
16318 </pre></code>
16319      * @param {Observable|Element} item The item to which to add a listener/listeners.
16320      * @param {Object|String} ename The event name, or an object containing event name properties.
16321      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
16322      * is the handler function.
16323      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
16324      * is the scope (<code>this</code> reference) in which the handler function is executed.
16325      * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this
16326      * is the {@link Ext.util.Observable#addListener addListener} options.
16327      */
16328     mon : function(item, ename, fn, scope, opt){
16329         this.createMons();
16330         if(Ext.isObject(ename)){
16331             var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
16332
16333             var o = ename;
16334             for(var e in o){
16335                 if(propRe.test(e)){
16336                     continue;
16337                 }
16338                 if(Ext.isFunction(o[e])){
16339                     // shared options
16340                     this.mons.push({
16341                         item: item, ename: e, fn: o[e], scope: o.scope
16342                     });
16343                     item.on(e, o[e], o.scope, o);
16344                 }else{
16345                     // individual options
16346                     this.mons.push({
16347                         item: item, ename: e, fn: o[e], scope: o.scope
16348                     });
16349                     item.on(e, o[e]);
16350                 }
16351             }
16352             return;
16353         }
16354
16355         this.mons.push({
16356             item: item, ename: ename, fn: fn, scope: scope
16357         });
16358         item.on(ename, fn, scope, opt);
16359     },
16360
16361     /**
16362      * Removes listeners that were added by the {@link #mon} method.
16363      * @param {Observable|Element} item The item from which to remove a listener/listeners.
16364      * @param {Object|String} ename The event name, or an object containing event name properties.
16365      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
16366      * is the handler function.
16367      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
16368      * is the scope (<code>this</code> reference) in which the handler function is executed.
16369      */
16370     mun : function(item, ename, fn, scope){
16371         var found, mon;
16372         this.createMons();
16373         for(var i = 0, len = this.mons.length; i < len; ++i){
16374             mon = this.mons[i];
16375             if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
16376                 this.mons.splice(i, 1);
16377                 item.un(ename, fn, scope);
16378                 found = true;
16379                 break;
16380             }
16381         }
16382         return found;
16383     },
16384
16385     /**
16386      * Returns the next component in the owning container
16387      * @return Ext.Component
16388      */
16389     nextSibling : function(){
16390         if(this.ownerCt){
16391             var index = this.ownerCt.items.indexOf(this);
16392             if(index != -1 && index+1 < this.ownerCt.items.getCount()){
16393                 return this.ownerCt.items.itemAt(index+1);
16394             }
16395         }
16396         return null;
16397     },
16398
16399     /**
16400      * Returns the previous component in the owning container
16401      * @return Ext.Component
16402      */
16403     previousSibling : function(){
16404         if(this.ownerCt){
16405             var index = this.ownerCt.items.indexOf(this);
16406             if(index > 0){
16407                 return this.ownerCt.items.itemAt(index-1);
16408             }
16409         }
16410         return null;
16411     },
16412
16413     /**
16414      * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
16415      * @return {Ext.Container} the Container which owns this Component.
16416      */
16417     getBubbleTarget : function(){
16418         return this.ownerCt;
16419     }
16420 });
16421
16422 Ext.reg('component', Ext.Component);/**
16423  * @class Ext.Action
16424  * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it
16425  * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI
16426  * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}
16427  * and {@link Ext.menu.Menu} components).</p>
16428  * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support
16429  * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),
16430  * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>
16431  * Example usage:<br>
16432  * <pre><code>
16433 // Define the shared action.  Each component below will have the same
16434 // display text and icon, and will display the same message on click.
16435 var action = new Ext.Action({
16436     {@link #text}: 'Do something',
16437     {@link #handler}: function(){
16438         Ext.Msg.alert('Click', 'You did something.');
16439     },
16440     {@link #iconCls}: 'do-something',
16441     {@link #itemId}: 'myAction'
16442 });
16443
16444 var panel = new Ext.Panel({
16445     title: 'Actions',
16446     width: 500,
16447     height: 300,
16448     tbar: [
16449         // Add the action directly to a toolbar as a menu button
16450         action,
16451         {
16452             text: 'Action Menu',
16453             // Add the action to a menu as a text item
16454             menu: [action]
16455         }
16456     ],
16457     items: [
16458         // Add the action to the panel body as a standard button
16459         new Ext.Button(action)
16460     ],
16461     renderTo: Ext.getBody()
16462 });
16463
16464 // Change the text for all components using the action
16465 action.setText('Something else');
16466
16467 // Reference an action through a container using the itemId
16468 var btn = panel.getComponent('myAction');
16469 var aRef = btn.baseAction;
16470 aRef.setText('New text');
16471 </code></pre>
16472  * @constructor
16473  * @param {Object} config The configuration options
16474  */
16475 Ext.Action = Ext.extend(Object, {
16476     /**
16477      * @cfg {String} text The text to set for all components using this action (defaults to '').
16478      */
16479     /**
16480      * @cfg {String} iconCls
16481      * The CSS class selector that specifies a background image to be used as the header icon for
16482      * all components using this action (defaults to '').
16483      * <p>An example of specifying a custom icon class would be something like:
16484      * </p><pre><code>
16485 // specify the property in the config for the class:
16486      ...
16487      iconCls: 'do-something'
16488
16489 // css class that specifies background image to be used as the icon image:
16490 .do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
16491 </code></pre>
16492      */
16493     /**
16494      * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).
16495      */
16496     /**
16497      * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).
16498      */
16499     /**
16500      * @cfg {Function} handler The function that will be invoked by each component tied to this action
16501      * when the component's primary event is triggered (defaults to undefined).
16502      */
16503     /**
16504      * @cfg {String} itemId
16505      * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.
16506      */
16507     /**
16508      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
16509      * <code>{@link #handler}</code> is executed. Defaults to this Button.
16510      */
16511
16512     constructor : function(config){
16513         this.initialConfig = config;
16514         this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
16515         this.items = [];
16516     },
16517     
16518     // private
16519     isAction : true,
16520
16521     /**
16522      * Sets the text to be displayed by all components using this action.
16523      * @param {String} text The text to display
16524      */
16525     setText : function(text){
16526         this.initialConfig.text = text;
16527         this.callEach('setText', [text]);
16528     },
16529
16530     /**
16531      * Gets the text currently displayed by all components using this action.
16532      */
16533     getText : function(){
16534         return this.initialConfig.text;
16535     },
16536
16537     /**
16538      * Sets the icon CSS class for all components using this action.  The class should supply
16539      * a background image that will be used as the icon image.
16540      * @param {String} cls The CSS class supplying the icon image
16541      */
16542     setIconClass : function(cls){
16543         this.initialConfig.iconCls = cls;
16544         this.callEach('setIconClass', [cls]);
16545     },
16546
16547     /**
16548      * Gets the icon CSS class currently used by all components using this action.
16549      */
16550     getIconClass : function(){
16551         return this.initialConfig.iconCls;
16552     },
16553
16554     /**
16555      * Sets the disabled state of all components using this action.  Shortcut method
16556      * for {@link #enable} and {@link #disable}.
16557      * @param {Boolean} disabled True to disable the component, false to enable it
16558      */
16559     setDisabled : function(v){
16560         this.initialConfig.disabled = v;
16561         this.callEach('setDisabled', [v]);
16562     },
16563
16564     /**
16565      * Enables all components using this action.
16566      */
16567     enable : function(){
16568         this.setDisabled(false);
16569     },
16570
16571     /**
16572      * Disables all components using this action.
16573      */
16574     disable : function(){
16575         this.setDisabled(true);
16576     },
16577
16578     /**
16579      * Returns true if the components using this action are currently disabled, else returns false.  
16580      */
16581     isDisabled : function(){
16582         return this.initialConfig.disabled;
16583     },
16584
16585     /**
16586      * Sets the hidden state of all components using this action.  Shortcut method
16587      * for <code>{@link #hide}</code> and <code>{@link #show}</code>.
16588      * @param {Boolean} hidden True to hide the component, false to show it
16589      */
16590     setHidden : function(v){
16591         this.initialConfig.hidden = v;
16592         this.callEach('setVisible', [!v]);
16593     },
16594
16595     /**
16596      * Shows all components using this action.
16597      */
16598     show : function(){
16599         this.setHidden(false);
16600     },
16601
16602     /**
16603      * Hides all components using this action.
16604      */
16605     hide : function(){
16606         this.setHidden(true);
16607     },
16608
16609     /**
16610      * Returns true if the components using this action are currently hidden, else returns false.  
16611      */
16612     isHidden : function(){
16613         return this.initialConfig.hidden;
16614     },
16615
16616     /**
16617      * Sets the function that will be called by each Component using this action when its primary event is triggered.
16618      * @param {Function} fn The function that will be invoked by the action's components.  The function
16619      * will be called with no arguments.
16620      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.
16621      */
16622     setHandler : function(fn, scope){
16623         this.initialConfig.handler = fn;
16624         this.initialConfig.scope = scope;
16625         this.callEach('setHandler', [fn, scope]);
16626     },
16627
16628     /**
16629      * Executes the specified function once for each Component currently tied to this action.  The function passed
16630      * in should accept a single argument that will be an object that supports the basic Action config/method interface.
16631      * @param {Function} fn The function to execute for each component
16632      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed.  Defaults to the Component.
16633      */
16634     each : function(fn, scope){
16635         Ext.each(this.items, fn, scope);
16636     },
16637
16638     // private
16639     callEach : function(fnName, args){
16640         var cs = this.items;
16641         for(var i = 0, len = cs.length; i < len; i++){
16642             cs[i][fnName].apply(cs[i], args);
16643         }
16644     },
16645
16646     // private
16647     addComponent : function(comp){
16648         this.items.push(comp);
16649         comp.on('destroy', this.removeComponent, this);
16650     },
16651
16652     // private
16653     removeComponent : function(comp){
16654         this.items.remove(comp);
16655     },
16656
16657     /**
16658      * Executes this action manually using the handler function specified in the original config object
16659      * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this
16660      * function will be passed on to the handler function.
16661      * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function
16662      * @param {Mixed} arg2 (optional)
16663      * @param {Mixed} etc... (optional)
16664      */
16665     execute : function(){
16666         this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);
16667     }
16668 });
16669 /**
16670  * @class Ext.Layer
16671  * @extends Ext.Element
16672  * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and
16673  * automatic maintaining of shadow/shim positions.
16674  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16675  * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
16676  * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
16677  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
16678  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16679  * @cfg {String} cls CSS class to add to the element
16680  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16681  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
16682  * @cfg {Boolean} useDisplay
16683  * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
16684  * to use css style <tt>'display:none;'</tt> to hide the Layer.
16685  * @constructor
16686  * @param {Object} config An object with config options.
16687  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16688  */
16689 (function(){
16690 Ext.Layer = function(config, existingEl){
16691     config = config || {};
16692     var dh = Ext.DomHelper;
16693     var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
16694     if(existingEl){
16695         this.dom = Ext.getDom(existingEl);
16696     }
16697     if(!this.dom){
16698         var o = config.dh || {tag: 'div', cls: 'x-layer'};
16699         this.dom = dh.append(pel, o);
16700     }
16701     if(config.cls){
16702         this.addClass(config.cls);
16703     }
16704     this.constrain = config.constrain !== false;
16705     this.setVisibilityMode(Ext.Element.VISIBILITY);
16706     if(config.id){
16707         this.id = this.dom.id = config.id;
16708     }else{
16709         this.id = Ext.id(this.dom);
16710     }
16711     this.zindex = config.zindex || this.getZIndex();
16712     this.position('absolute', this.zindex);
16713     if(config.shadow){
16714         this.shadowOffset = config.shadowOffset || 4;
16715         this.shadow = new Ext.Shadow({
16716             offset : this.shadowOffset,
16717             mode : config.shadow
16718         });
16719     }else{
16720         this.shadowOffset = 0;
16721     }
16722     this.useShim = config.shim !== false && Ext.useShims;
16723     this.useDisplay = config.useDisplay;
16724     this.hide();
16725 };
16726
16727 var supr = Ext.Element.prototype;
16728
16729 // shims are shared among layer to keep from having 100 iframes
16730 var shims = [];
16731
16732 Ext.extend(Ext.Layer, Ext.Element, {
16733
16734     getZIndex : function(){
16735         return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000;
16736     },
16737
16738     getShim : function(){
16739         if(!this.useShim){
16740             return null;
16741         }
16742         if(this.shim){
16743             return this.shim;
16744         }
16745         var shim = shims.shift();
16746         if(!shim){
16747             shim = this.createShim();
16748             shim.enableDisplayMode('block');
16749             shim.dom.style.display = 'none';
16750             shim.dom.style.visibility = 'visible';
16751         }
16752         var pn = this.dom.parentNode;
16753         if(shim.dom.parentNode != pn){
16754             pn.insertBefore(shim.dom, this.dom);
16755         }
16756         shim.setStyle('z-index', this.getZIndex()-2);
16757         this.shim = shim;
16758         return shim;
16759     },
16760
16761     hideShim : function(){
16762         if(this.shim){
16763             this.shim.setDisplayed(false);
16764             shims.push(this.shim);
16765             delete this.shim;
16766         }
16767     },
16768
16769     disableShadow : function(){
16770         if(this.shadow){
16771             this.shadowDisabled = true;
16772             this.shadow.hide();
16773             this.lastShadowOffset = this.shadowOffset;
16774             this.shadowOffset = 0;
16775         }
16776     },
16777
16778     enableShadow : function(show){
16779         if(this.shadow){
16780             this.shadowDisabled = false;
16781             this.shadowOffset = this.lastShadowOffset;
16782             delete this.lastShadowOffset;
16783             if(show){
16784                 this.sync(true);
16785             }
16786         }
16787     },
16788
16789     // private
16790     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16791     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16792     sync : function(doShow){
16793         var shadow = this.shadow;
16794         if(!this.updating && this.isVisible() && (shadow || this.useShim)){
16795             var shim = this.getShim(),
16796                 w = this.getWidth(),
16797                 h = this.getHeight(),
16798                 l = this.getLeft(true),
16799                 t = this.getTop(true);
16800
16801             if(shadow && !this.shadowDisabled){
16802                 if(doShow && !shadow.isVisible()){
16803                     shadow.show(this);
16804                 }else{
16805                     shadow.realign(l, t, w, h);
16806                 }
16807                 if(shim){
16808                     if(doShow){
16809                        shim.show();
16810                     }
16811                     // fit the shim behind the shadow, so it is shimmed too
16812                     var shadowAdj = shadow.el.getXY(), shimStyle = shim.dom.style,
16813                         shadowSize = shadow.el.getSize();
16814                     shimStyle.left = (shadowAdj[0])+'px';
16815                     shimStyle.top = (shadowAdj[1])+'px';
16816                     shimStyle.width = (shadowSize.width)+'px';
16817                     shimStyle.height = (shadowSize.height)+'px';
16818                 }
16819             }else if(shim){
16820                 if(doShow){
16821                    shim.show();
16822                 }
16823                 shim.setSize(w, h);
16824                 shim.setLeftTop(l, t);
16825             }
16826         }
16827     },
16828
16829     // private
16830     destroy : function(){
16831         this.hideShim();
16832         if(this.shadow){
16833             this.shadow.hide();
16834         }
16835         this.removeAllListeners();
16836         Ext.removeNode(this.dom);
16837         delete this.dom;
16838     },
16839
16840     remove : function(){
16841         this.destroy();
16842     },
16843
16844     // private
16845     beginUpdate : function(){
16846         this.updating = true;
16847     },
16848
16849     // private
16850     endUpdate : function(){
16851         this.updating = false;
16852         this.sync(true);
16853     },
16854
16855     // private
16856     hideUnders : function(negOffset){
16857         if(this.shadow){
16858             this.shadow.hide();
16859         }
16860         this.hideShim();
16861     },
16862
16863     // private
16864     constrainXY : function(){
16865         if(this.constrain){
16866             var vw = Ext.lib.Dom.getViewWidth(),
16867                 vh = Ext.lib.Dom.getViewHeight();
16868             var s = Ext.getDoc().getScroll();
16869
16870             var xy = this.getXY();
16871             var x = xy[0], y = xy[1];
16872             var so = this.shadowOffset;
16873             var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so;
16874             // only move it if it needs it
16875             var moved = false;
16876             // first validate right/bottom
16877             if((x + w) > vw+s.left){
16878                 x = vw - w - so;
16879                 moved = true;
16880             }
16881             if((y + h) > vh+s.top){
16882                 y = vh - h - so;
16883                 moved = true;
16884             }
16885             // then make sure top/left isn't negative
16886             if(x < s.left){
16887                 x = s.left;
16888                 moved = true;
16889             }
16890             if(y < s.top){
16891                 y = s.top;
16892                 moved = true;
16893             }
16894             if(moved){
16895                 if(this.avoidY){
16896                     var ay = this.avoidY;
16897                     if(y <= ay && (y+h) >= ay){
16898                         y = ay-h-5;
16899                     }
16900                 }
16901                 xy = [x, y];
16902                 this.storeXY(xy);
16903                 supr.setXY.call(this, xy);
16904                 this.sync();
16905             }
16906         }
16907         return this;
16908     },
16909
16910     isVisible : function(){
16911         return this.visible;
16912     },
16913
16914     // private
16915     showAction : function(){
16916         this.visible = true; // track visibility to prevent getStyle calls
16917         if(this.useDisplay === true){
16918             this.setDisplayed('');
16919         }else if(this.lastXY){
16920             supr.setXY.call(this, this.lastXY);
16921         }else if(this.lastLT){
16922             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16923         }
16924     },
16925
16926     // private
16927     hideAction : function(){
16928         this.visible = false;
16929         if(this.useDisplay === true){
16930             this.setDisplayed(false);
16931         }else{
16932             this.setLeftTop(-10000,-10000);
16933         }
16934     },
16935
16936     // overridden Element method
16937     setVisible : function(v, a, d, c, e){
16938         if(v){
16939             this.showAction();
16940         }
16941         if(a && v){
16942             var cb = function(){
16943                 this.sync(true);
16944                 if(c){
16945                     c();
16946                 }
16947             }.createDelegate(this);
16948             supr.setVisible.call(this, true, true, d, cb, e);
16949         }else{
16950             if(!v){
16951                 this.hideUnders(true);
16952             }
16953             var cb = c;
16954             if(a){
16955                 cb = function(){
16956                     this.hideAction();
16957                     if(c){
16958                         c();
16959                     }
16960                 }.createDelegate(this);
16961             }
16962             supr.setVisible.call(this, v, a, d, cb, e);
16963             if(v){
16964                 this.sync(true);
16965             }else if(!a){
16966                 this.hideAction();
16967             }
16968         }
16969         return this;
16970     },
16971
16972     storeXY : function(xy){
16973         delete this.lastLT;
16974         this.lastXY = xy;
16975     },
16976
16977     storeLeftTop : function(left, top){
16978         delete this.lastXY;
16979         this.lastLT = [left, top];
16980     },
16981
16982     // private
16983     beforeFx : function(){
16984         this.beforeAction();
16985         return Ext.Layer.superclass.beforeFx.apply(this, arguments);
16986     },
16987
16988     // private
16989     afterFx : function(){
16990         Ext.Layer.superclass.afterFx.apply(this, arguments);
16991         this.sync(this.isVisible());
16992     },
16993
16994     // private
16995     beforeAction : function(){
16996         if(!this.updating && this.shadow){
16997             this.shadow.hide();
16998         }
16999     },
17000
17001     // overridden Element method
17002     setLeft : function(left){
17003         this.storeLeftTop(left, this.getTop(true));
17004         supr.setLeft.apply(this, arguments);
17005         this.sync();
17006         return this;
17007     },
17008
17009     setTop : function(top){
17010         this.storeLeftTop(this.getLeft(true), top);
17011         supr.setTop.apply(this, arguments);
17012         this.sync();
17013         return this;
17014     },
17015
17016     setLeftTop : function(left, top){
17017         this.storeLeftTop(left, top);
17018         supr.setLeftTop.apply(this, arguments);
17019         this.sync();
17020         return this;
17021     },
17022
17023     setXY : function(xy, a, d, c, e){
17024         this.fixDisplay();
17025         this.beforeAction();
17026         this.storeXY(xy);
17027         var cb = this.createCB(c);
17028         supr.setXY.call(this, xy, a, d, cb, e);
17029         if(!a){
17030             cb();
17031         }
17032         return this;
17033     },
17034
17035     // private
17036     createCB : function(c){
17037         var el = this;
17038         return function(){
17039             el.constrainXY();
17040             el.sync(true);
17041             if(c){
17042                 c();
17043             }
17044         };
17045     },
17046
17047     // overridden Element method
17048     setX : function(x, a, d, c, e){
17049         this.setXY([x, this.getY()], a, d, c, e);
17050         return this;
17051     },
17052
17053     // overridden Element method
17054     setY : function(y, a, d, c, e){
17055         this.setXY([this.getX(), y], a, d, c, e);
17056         return this;
17057     },
17058
17059     // overridden Element method
17060     setSize : function(w, h, a, d, c, e){
17061         this.beforeAction();
17062         var cb = this.createCB(c);
17063         supr.setSize.call(this, w, h, a, d, cb, e);
17064         if(!a){
17065             cb();
17066         }
17067         return this;
17068     },
17069
17070     // overridden Element method
17071     setWidth : function(w, a, d, c, e){
17072         this.beforeAction();
17073         var cb = this.createCB(c);
17074         supr.setWidth.call(this, w, a, d, cb, e);
17075         if(!a){
17076             cb();
17077         }
17078         return this;
17079     },
17080
17081     // overridden Element method
17082     setHeight : function(h, a, d, c, e){
17083         this.beforeAction();
17084         var cb = this.createCB(c);
17085         supr.setHeight.call(this, h, a, d, cb, e);
17086         if(!a){
17087             cb();
17088         }
17089         return this;
17090     },
17091
17092     // overridden Element method
17093     setBounds : function(x, y, w, h, a, d, c, e){
17094         this.beforeAction();
17095         var cb = this.createCB(c);
17096         if(!a){
17097             this.storeXY([x, y]);
17098             supr.setXY.call(this, [x, y]);
17099             supr.setSize.call(this, w, h, a, d, cb, e);
17100             cb();
17101         }else{
17102             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17103         }
17104         return this;
17105     },
17106
17107     /**
17108      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17109      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17110      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17111      * @param {Number} zindex The new z-index to set
17112      * @return {this} The Layer
17113      */
17114     setZIndex : function(zindex){
17115         this.zindex = zindex;
17116         this.setStyle('z-index', zindex + 2);
17117         if(this.shadow){
17118             this.shadow.setZIndex(zindex + 1);
17119         }
17120         if(this.shim){
17121             this.shim.setStyle('z-index', zindex);
17122         }
17123         return this;
17124     }
17125 });
17126 })();
17127 /**
17128  * @class Ext.Shadow
17129  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
17130  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
17131  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
17132  * @constructor
17133  * Create a new Shadow
17134  * @param {Object} config The config object
17135  */
17136 Ext.Shadow = function(config){
17137     Ext.apply(this, config);
17138     if(typeof this.mode != "string"){
17139         this.mode = this.defaultMode;
17140     }
17141     var o = this.offset, a = {h: 0};
17142     var rad = Math.floor(this.offset/2);
17143     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
17144         case "drop":
17145             a.w = 0;
17146             a.l = a.t = o;
17147             a.t -= 1;
17148             if(Ext.isIE){
17149                 a.l -= this.offset + rad;
17150                 a.t -= this.offset + rad;
17151                 a.w -= rad;
17152                 a.h -= rad;
17153                 a.t += 1;
17154             }
17155         break;
17156         case "sides":
17157             a.w = (o*2);
17158             a.l = -o;
17159             a.t = o-1;
17160             if(Ext.isIE){
17161                 a.l -= (this.offset - rad);
17162                 a.t -= this.offset + rad;
17163                 a.l += 1;
17164                 a.w -= (this.offset - rad)*2;
17165                 a.w -= rad + 1;
17166                 a.h -= 1;
17167             }
17168         break;
17169         case "frame":
17170             a.w = a.h = (o*2);
17171             a.l = a.t = -o;
17172             a.t += 1;
17173             a.h -= 2;
17174             if(Ext.isIE){
17175                 a.l -= (this.offset - rad);
17176                 a.t -= (this.offset - rad);
17177                 a.l += 1;
17178                 a.w -= (this.offset + rad + 1);
17179                 a.h -= (this.offset + rad);
17180                 a.h += 1;
17181             }
17182         break;
17183     };
17184
17185     this.adjusts = a;
17186 };
17187
17188 Ext.Shadow.prototype = {
17189     /**
17190      * @cfg {String} mode
17191      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
17192      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
17193      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
17194      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
17195      * </ul></div>
17196      */
17197     /**
17198      * @cfg {String} offset
17199      * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
17200      */
17201     offset: 4,
17202
17203     // private
17204     defaultMode: "drop",
17205
17206     /**
17207      * Displays the shadow under the target element
17208      * @param {Mixed} targetEl The id or element under which the shadow should display
17209      */
17210     show : function(target){
17211         target = Ext.get(target);
17212         if(!this.el){
17213             this.el = Ext.Shadow.Pool.pull();
17214             if(this.el.dom.nextSibling != target.dom){
17215                 this.el.insertBefore(target);
17216             }
17217         }
17218         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
17219         if(Ext.isIE){
17220             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
17221         }
17222         this.realign(
17223             target.getLeft(true),
17224             target.getTop(true),
17225             target.getWidth(),
17226             target.getHeight()
17227         );
17228         this.el.dom.style.display = "block";
17229     },
17230
17231     /**
17232      * Returns true if the shadow is visible, else false
17233      */
17234     isVisible : function(){
17235         return this.el ? true : false;  
17236     },
17237
17238     /**
17239      * Direct alignment when values are already available. Show must be called at least once before
17240      * calling this method to ensure it is initialized.
17241      * @param {Number} left The target element left position
17242      * @param {Number} top The target element top position
17243      * @param {Number} width The target element width
17244      * @param {Number} height The target element height
17245      */
17246     realign : function(l, t, w, h){
17247         if(!this.el){
17248             return;
17249         }
17250         var a = this.adjusts, d = this.el.dom, s = d.style;
17251         var iea = 0;
17252         s.left = (l+a.l)+"px";
17253         s.top = (t+a.t)+"px";
17254         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
17255         if(s.width != sws || s.height != shs){
17256             s.width = sws;
17257             s.height = shs;
17258             if(!Ext.isIE){
17259                 var cn = d.childNodes;
17260                 var sww = Math.max(0, (sw-12))+"px";
17261                 cn[0].childNodes[1].style.width = sww;
17262                 cn[1].childNodes[1].style.width = sww;
17263                 cn[2].childNodes[1].style.width = sww;
17264                 cn[1].style.height = Math.max(0, (sh-12))+"px";
17265             }
17266         }
17267     },
17268
17269     /**
17270      * Hides this shadow
17271      */
17272     hide : function(){
17273         if(this.el){
17274             this.el.dom.style.display = "none";
17275             Ext.Shadow.Pool.push(this.el);
17276             delete this.el;
17277         }
17278     },
17279
17280     /**
17281      * Adjust the z-index of this shadow
17282      * @param {Number} zindex The new z-index
17283      */
17284     setZIndex : function(z){
17285         this.zIndex = z;
17286         if(this.el){
17287             this.el.setStyle("z-index", z);
17288         }
17289     }
17290 };
17291
17292 // Private utility class that manages the internal Shadow cache
17293 Ext.Shadow.Pool = function(){
17294     var p = [];
17295     var markup = Ext.isIE ?
17296                  '<div class="x-ie-shadow"></div>' :
17297                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
17298     return {
17299         pull : function(){
17300             var sh = p.shift();
17301             if(!sh){
17302                 sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
17303                 sh.autoBoxAdjust = false;
17304             }
17305             return sh;
17306         },
17307
17308         push : function(sh){
17309             p.push(sh);
17310         }
17311     };
17312 }();/**
17313  * @class Ext.BoxComponent
17314  * @extends Ext.Component
17315  * <p>Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.</p>
17316  * <p>BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly
17317  * within the Component rendering model.</p>
17318  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing
17319  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,
17320  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>
17321  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the
17322  * element to reference:<pre><code>
17323 var pageHeader = new Ext.BoxComponent({
17324     el: 'my-header-div'
17325 });</code></pre>
17326  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>
17327  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the
17328  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a
17329  * {@link Ext.DomHelper DomHelper} specification:<pre><code>
17330 var myImage = new Ext.BoxComponent({
17331     autoEl: {
17332         tag: 'img',
17333         src: '/images/my-image.jpg'
17334     }
17335 });</code></pre></p>
17336  * @constructor
17337  * @param {Ext.Element/String/Object} config The configuration options.
17338  * @xtype box
17339  */
17340 Ext.BoxComponent = Ext.extend(Ext.Component, {
17341
17342     // Configs below are used for all Components when rendered by BoxLayout.
17343     /**
17344      * @cfg {Number} flex
17345      * <p><b>Note</b>: this config is only used when this Component is rendered
17346      * by a Container which has been configured to use a <b>{@link Ext.layout.BoxLayout BoxLayout}.</b>
17347      * Each child Component with a <code>flex</code> property will be flexed either vertically (by a VBoxLayout)
17348      * or horizontally (by an HBoxLayout) according to the item's <b>relative</b> <code>flex</code> value
17349      * compared to the sum of all Components with <code>flex</flex> value specified. Any child items that have
17350      * either a <code>flex = 0</code> or <code>flex = undefined</code> will not be 'flexed' (the initial size will not be changed).
17351      */
17352     // Configs below are used for all Components when rendered by AnchorLayout.
17353     /**
17354      * @cfg {String} anchor <p><b>Note</b>: this config is only used when this Component is rendered
17355      * by a Container which has been configured to use an <b>{@link Ext.layout.AnchorLayout AnchorLayout} (or subclass thereof).</b>
17356      * based layout manager, for example:<div class="mdetail-params"><ul>
17357      * <li>{@link Ext.form.FormPanel}</li>
17358      * <li>specifying <code>layout: 'anchor' // or 'form', or 'absolute'</code></li>
17359      * </ul></div></p>
17360      * <p>See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.</p>
17361      */
17362     // tabTip config is used when a BoxComponent is a child of a TabPanel
17363     /**
17364      * @cfg {String} tabTip
17365      * <p><b>Note</b>: this config is only used when this BoxComponent is a child item of a TabPanel.</p>
17366      * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over
17367      * the associated tab selector element. {@link Ext.QuickTips}.init()
17368      * must be called in order for the tips to render.
17369      */
17370     // Configs below are used for all Components when rendered by BorderLayout.
17371     /**
17372      * @cfg {String} region <p><b>Note</b>: this config is only used when this BoxComponent is rendered
17373      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
17374      * layout manager (e.g. specifying <tt>layout:'border'</tt>).</p><br>
17375      * <p>See {@link Ext.layout.BorderLayout} also.</p>
17376      */
17377     // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout.
17378     /**
17379      * @cfg {Object} margins <p><b>Note</b>: this config is only used when this BoxComponent is rendered
17380      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
17381      * or one of the two <b>{@link Ext.layout.BoxLayout BoxLayout} subclasses.</b></p>
17382      * <p>An object containing margins to apply to this BoxComponent in the
17383      * format:</p><pre><code>
17384 {
17385     top: (top margin),
17386     right: (right margin),
17387     bottom: (bottom margin),
17388     left: (left margin)
17389 }</code></pre>
17390      * <p>May also be a string containing space-separated, numeric margin values. The order of the
17391      * sides associated with each value matches the way CSS processes margin values:</p>
17392      * <p><div class="mdetail-params"><ul>
17393      * <li>If there is only one value, it applies to all sides.</li>
17394      * <li>If there are two values, the top and bottom borders are set to the first value and the
17395      * right and left are set to the second.</li>
17396      * <li>If there are three values, the top is set to the first value, the left and right are set
17397      * to the second, and the bottom is set to the third.</li>
17398      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
17399      * </ul></div></p>
17400      * <p>Defaults to:</p><pre><code>
17401      * {top:0, right:0, bottom:0, left:0}
17402      * </code></pre>
17403      */
17404     /**
17405      * @cfg {Number} x
17406      * The local x (left) coordinate for this component if contained within a positioning container.
17407      */
17408     /**
17409      * @cfg {Number} y
17410      * The local y (top) coordinate for this component if contained within a positioning container.
17411      */
17412     /**
17413      * @cfg {Number} pageX
17414      * The page level x coordinate for this component if contained within a positioning container.
17415      */
17416     /**
17417      * @cfg {Number} pageY
17418      * The page level y coordinate for this component if contained within a positioning container.
17419      */
17420     /**
17421      * @cfg {Number} height
17422      * The height of this component in pixels (defaults to auto).
17423      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
17424      */
17425     /**
17426      * @cfg {Number} width
17427      * The width of this component in pixels (defaults to auto).
17428      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
17429      */
17430     /**
17431      * @cfg {Number} boxMinHeight
17432      * <p>The minimum value in pixels which this BoxComponent will set its height to.</p>
17433      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17434      */
17435     /**
17436      * @cfg {Number} boxMinWidth
17437      * <p>The minimum value in pixels which this BoxComponent will set its width to.</p>
17438      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17439      */
17440     /**
17441      * @cfg {Number} boxMaxHeight
17442      * <p>The maximum value in pixels which this BoxComponent will set its height to.</p>
17443      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17444      */
17445     /**
17446      * @cfg {Number} boxMaxWidth
17447      * <p>The maximum value in pixels which this BoxComponent will set its width to.</p>
17448      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17449      */
17450     /**
17451      * @cfg {Boolean} autoHeight
17452      * <p>True to use height:'auto', false to use fixed height (or allow it to be managed by its parent
17453      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
17454      * <p><b>Note</b>: Although many components inherit this config option, not all will
17455      * function as expected with a height of 'auto'. Setting autoHeight:true means that the
17456      * browser will manage height based on the element's contents, and that Ext will not manage it at all.</p>
17457      * <p>If the <i>browser</i> is managing the height, be aware that resizes performed by the browser in response
17458      * to changes within the structure of the Component cannot be detected. Therefore changes to the height might
17459      * result in elements needing to be synchronized with the new height. Example:</p><pre><code>
17460 var w = new Ext.Window({
17461     title: 'Window',
17462     width: 600,
17463     autoHeight: true,
17464     items: {
17465         title: 'Collapse Me',
17466         height: 400,
17467         collapsible: true,
17468         border: false,
17469         listeners: {
17470             beforecollapse: function() {
17471                 w.el.shadow.hide();
17472             },
17473             beforeexpand: function() {
17474                 w.el.shadow.hide();
17475             },
17476             collapse: function() {
17477                 w.syncShadow();
17478             },
17479             expand: function() {
17480                 w.syncShadow();
17481             }
17482         }
17483     }
17484 }).show();
17485 </code></pre>
17486      */
17487     /**
17488      * @cfg {Boolean} autoWidth
17489      * <p>True to use width:'auto', false to use fixed width (or allow it to be managed by its parent
17490      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
17491      * <p><b>Note</b>: Although many components  inherit this config option, not all will
17492      * function as expected with a width of 'auto'. Setting autoWidth:true means that the
17493      * browser will manage width based on the element's contents, and that Ext will not manage it at all.</p>
17494      * <p>If the <i>browser</i> is managing the width, be aware that resizes performed by the browser in response
17495      * to changes within the structure of the Component cannot be detected. Therefore changes to the width might
17496      * result in elements needing to be synchronized with the new width. For example, where the target element is:</p><pre><code>
17497 &lt;div id='grid-container' style='margin-left:25%;width:50%'>&lt;/div>
17498 </code></pre>
17499      * A Panel rendered into that target element must listen for browser window resize in order to relay its
17500       * child items when the browser changes its width:<pre><code>
17501 var myPanel = new Ext.Panel({
17502     renderTo: 'grid-container',
17503     monitorResize: true, // relay on browser resize
17504     title: 'Panel',
17505     height: 400,
17506     autoWidth: true,
17507     layout: 'hbox',
17508     layoutConfig: {
17509         align: 'stretch'
17510     },
17511     defaults: {
17512         flex: 1
17513     },
17514     items: [{
17515         title: 'Box 1',
17516     }, {
17517         title: 'Box 2'
17518     }, {
17519         title: 'Box 3'
17520     }],
17521 });
17522 </code></pre>
17523      */
17524     /**
17525      * @cfg {Boolean} autoScroll
17526      * <code>true</code> to use overflow:'auto' on the components layout element and show scroll bars automatically when
17527      * necessary, <code>false</code> to clip any overflowing content (defaults to <code>false</code>).
17528      */
17529
17530     /* // private internal config
17531      * {Boolean} deferHeight
17532      * True to defer height calculations to an external component, false to allow this component to set its own
17533      * height (defaults to false).
17534      */
17535
17536     // private
17537     initComponent : function(){
17538         Ext.BoxComponent.superclass.initComponent.call(this);
17539         this.addEvents(
17540             /**
17541              * @event resize
17542              * Fires after the component is resized.
17543              * @param {Ext.Component} this
17544              * @param {Number} adjWidth The box-adjusted width that was set
17545              * @param {Number} adjHeight The box-adjusted height that was set
17546              * @param {Number} rawWidth The width that was originally specified
17547              * @param {Number} rawHeight The height that was originally specified
17548              */
17549             'resize',
17550             /**
17551              * @event move
17552              * Fires after the component is moved.
17553              * @param {Ext.Component} this
17554              * @param {Number} x The new x position
17555              * @param {Number} y The new y position
17556              */
17557             'move'
17558         );
17559     },
17560
17561     // private, set in afterRender to signify that the component has been rendered
17562     boxReady : false,
17563     // private, used to defer height settings to subclasses
17564     deferHeight: false,
17565
17566     /**
17567      * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept
17568      * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
17569      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
17570      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17571      * <li>A String used to set the CSS width style.</li>
17572      * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
17573      * <li><code>undefined</code> to leave the width unchanged.</li>
17574      * </ul></div>
17575      * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
17576      * This may be one of:<div class="mdetail-params"><ul>
17577      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17578      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
17579      * <li><code>undefined</code> to leave the height unchanged.</li>
17580      * </ul></div>
17581      * @return {Ext.BoxComponent} this
17582      */
17583     setSize : function(w, h){
17584
17585         // support for standard size objects
17586         if(typeof w == 'object'){
17587             h = w.height;
17588             w = w.width;
17589         }
17590         if (Ext.isDefined(w) && Ext.isDefined(this.boxMinWidth) && (w < this.boxMinWidth)) {
17591             w = this.boxMinWidth;
17592         }
17593         if (Ext.isDefined(h) && Ext.isDefined(this.boxMinHeight) && (h < this.boxMinHeight)) {
17594             h = this.boxMinHeight;
17595         }
17596         if (Ext.isDefined(w) && Ext.isDefined(this.boxMaxWidth) && (w > this.boxMaxWidth)) {
17597             w = this.boxMaxWidth;
17598         }
17599         if (Ext.isDefined(h) && Ext.isDefined(this.boxMaxHeight) && (h > this.boxMaxHeight)) {
17600             h = this.boxMaxHeight;
17601         }
17602         // not rendered
17603         if(!this.boxReady){
17604             this.width  = w;
17605             this.height = h;
17606             return this;
17607         }
17608
17609         // prevent recalcs when not needed
17610         if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17611             return this;
17612         }
17613         this.lastSize = {width: w, height: h};
17614         var adj = this.adjustSize(w, h),
17615             aw = adj.width,
17616             ah = adj.height,
17617             rz;
17618         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17619             rz = this.getResizeEl();
17620             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17621                 rz.setSize(aw, ah);
17622             }else if(!this.deferHeight && ah !== undefined){
17623                 rz.setHeight(ah);
17624             }else if(aw !== undefined){
17625                 rz.setWidth(aw);
17626             }
17627             this.onResize(aw, ah, w, h);
17628             this.fireEvent('resize', this, aw, ah, w, h);
17629         }
17630         return this;
17631     },
17632
17633     /**
17634      * Sets the width of the component.  This method fires the {@link #resize} event.
17635      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
17636      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
17637      * <li>A String used to set the CSS width style.</li>
17638      * </ul></div>
17639      * @return {Ext.BoxComponent} this
17640      */
17641     setWidth : function(width){
17642         return this.setSize(width);
17643     },
17644
17645     /**
17646      * Sets the height of the component.  This method fires the {@link #resize} event.
17647      * @param {Mixed} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
17648      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
17649      * <li>A String used to set the CSS height style.</li>
17650      * <li><i>undefined</i> to leave the height unchanged.</li>
17651      * </ul></div>
17652      * @return {Ext.BoxComponent} this
17653      */
17654     setHeight : function(height){
17655         return this.setSize(undefined, height);
17656     },
17657
17658     /**
17659      * Gets the current size of the component's underlying element.
17660      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17661      */
17662     getSize : function(){
17663         return this.getResizeEl().getSize();
17664     },
17665
17666     /**
17667      * Gets the current width of the component's underlying element.
17668      * @return {Number}
17669      */
17670     getWidth : function(){
17671         return this.getResizeEl().getWidth();
17672     },
17673
17674     /**
17675      * Gets the current height of the component's underlying element.
17676      * @return {Number}
17677      */
17678     getHeight : function(){
17679         return this.getResizeEl().getHeight();
17680     },
17681
17682     /**
17683      * Gets the current size of the component's underlying element, including space taken by its margins.
17684      * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)}
17685      */
17686     getOuterSize : function(){
17687         var el = this.getResizeEl();
17688         return {width: el.getWidth() + el.getMargins('lr'),
17689                 height: el.getHeight() + el.getMargins('tb')};
17690     },
17691
17692     /**
17693      * Gets the current XY position of the component's underlying element.
17694      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17695      * @return {Array} The XY position of the element (e.g., [100, 200])
17696      */
17697     getPosition : function(local){
17698         var el = this.getPositionEl();
17699         if(local === true){
17700             return [el.getLeft(true), el.getTop(true)];
17701         }
17702         return this.xy || el.getXY();
17703     },
17704
17705     /**
17706      * Gets the current box measurements of the component's underlying element.
17707      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17708      * @return {Object} box An object in the format {x, y, width, height}
17709      */
17710     getBox : function(local){
17711         var pos = this.getPosition(local);
17712         var s = this.getSize();
17713         s.x = pos[0];
17714         s.y = pos[1];
17715         return s;
17716     },
17717
17718     /**
17719      * Sets the current box measurements of the component's underlying element.
17720      * @param {Object} box An object in the format {x, y, width, height}
17721      * @return {Ext.BoxComponent} this
17722      */
17723     updateBox : function(box){
17724         this.setSize(box.width, box.height);
17725         this.setPagePosition(box.x, box.y);
17726         return this;
17727     },
17728
17729     /**
17730      * <p>Returns the outermost Element of this Component which defines the Components overall size.</p>
17731      * <p><i>Usually</i> this will return the same Element as <code>{@link #getEl}</code>,
17732      * but in some cases, a Component may have some more wrapping Elements around its main
17733      * active Element.</p>
17734      * <p>An example is a ComboBox. It is encased in a <i>wrapping</i> Element which
17735      * contains both the <code>&lt;input></code> Element (which is what would be returned
17736      * by its <code>{@link #getEl}</code> method, <i>and</i> the trigger button Element.
17737      * This Element is returned as the <code>resizeEl</code>.
17738      * @return {Ext.Element} The Element which is to be resized by size managing layouts.
17739      */
17740     getResizeEl : function(){
17741         return this.resizeEl || this.el;
17742     },
17743
17744     /**
17745      * Sets the overflow on the content element of the component.
17746      * @param {Boolean} scroll True to allow the Component to auto scroll.
17747      * @return {Ext.BoxComponent} this
17748      */
17749     setAutoScroll : function(scroll){
17750         if(this.rendered){
17751             this.getContentTarget().setOverflow(scroll ? 'auto' : '');
17752         }
17753         this.autoScroll = scroll;
17754         return this;
17755     },
17756
17757     /**
17758      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17759      * This method fires the {@link #move} event.
17760      * @param {Number} left The new left
17761      * @param {Number} top The new top
17762      * @return {Ext.BoxComponent} this
17763      */
17764     setPosition : function(x, y){
17765         if(x && typeof x[1] == 'number'){
17766             y = x[1];
17767             x = x[0];
17768         }
17769         this.x = x;
17770         this.y = y;
17771         if(!this.boxReady){
17772             return this;
17773         }
17774         var adj = this.adjustPosition(x, y);
17775         var ax = adj.x, ay = adj.y;
17776
17777         var el = this.getPositionEl();
17778         if(ax !== undefined || ay !== undefined){
17779             if(ax !== undefined && ay !== undefined){
17780                 el.setLeftTop(ax, ay);
17781             }else if(ax !== undefined){
17782                 el.setLeft(ax);
17783             }else if(ay !== undefined){
17784                 el.setTop(ay);
17785             }
17786             this.onPosition(ax, ay);
17787             this.fireEvent('move', this, ax, ay);
17788         }
17789         return this;
17790     },
17791
17792     /**
17793      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17794      * This method fires the {@link #move} event.
17795      * @param {Number} x The new x position
17796      * @param {Number} y The new y position
17797      * @return {Ext.BoxComponent} this
17798      */
17799     setPagePosition : function(x, y){
17800         if(x && typeof x[1] == 'number'){
17801             y = x[1];
17802             x = x[0];
17803         }
17804         this.pageX = x;
17805         this.pageY = y;
17806         if(!this.boxReady){
17807             return;
17808         }
17809         if(x === undefined || y === undefined){ // cannot translate undefined points
17810             return;
17811         }
17812         var p = this.getPositionEl().translatePoints(x, y);
17813         this.setPosition(p.left, p.top);
17814         return this;
17815     },
17816
17817     // private
17818     afterRender : function(){
17819         Ext.BoxComponent.superclass.afterRender.call(this);
17820         if(this.resizeEl){
17821             this.resizeEl = Ext.get(this.resizeEl);
17822         }
17823         if(this.positionEl){
17824             this.positionEl = Ext.get(this.positionEl);
17825         }
17826         this.boxReady = true;
17827         Ext.isDefined(this.autoScroll) && this.setAutoScroll(this.autoScroll);
17828         this.setSize(this.width, this.height);
17829         if(this.x || this.y){
17830             this.setPosition(this.x, this.y);
17831         }else if(this.pageX || this.pageY){
17832             this.setPagePosition(this.pageX, this.pageY);
17833         }
17834     },
17835
17836     /**
17837      * Force the component's size to recalculate based on the underlying element's current height and width.
17838      * @return {Ext.BoxComponent} this
17839      */
17840     syncSize : function(){
17841         delete this.lastSize;
17842         this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight());
17843         return this;
17844     },
17845
17846     /* // protected
17847      * Called after the component is resized, this method is empty by default but can be implemented by any
17848      * subclass that needs to perform custom logic after a resize occurs.
17849      * @param {Number} adjWidth The box-adjusted width that was set
17850      * @param {Number} adjHeight The box-adjusted height that was set
17851      * @param {Number} rawWidth The width that was originally specified
17852      * @param {Number} rawHeight The height that was originally specified
17853      */
17854     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17855     },
17856
17857     /* // protected
17858      * Called after the component is moved, this method is empty by default but can be implemented by any
17859      * subclass that needs to perform custom logic after a move occurs.
17860      * @param {Number} x The new x position
17861      * @param {Number} y The new y position
17862      */
17863     onPosition : function(x, y){
17864
17865     },
17866
17867     // private
17868     adjustSize : function(w, h){
17869         if(this.autoWidth){
17870             w = 'auto';
17871         }
17872         if(this.autoHeight){
17873             h = 'auto';
17874         }
17875         return {width : w, height: h};
17876     },
17877
17878     // private
17879     adjustPosition : function(x, y){
17880         return {x : x, y: y};
17881     }
17882 });
17883 Ext.reg('box', Ext.BoxComponent);
17884
17885
17886 /**
17887  * @class Ext.Spacer
17888  * @extends Ext.BoxComponent
17889  * <p>Used to provide a sizable space in a layout.</p>
17890  * @constructor
17891  * @param {Object} config
17892  */
17893 Ext.Spacer = Ext.extend(Ext.BoxComponent, {
17894     autoEl:'div'
17895 });
17896 Ext.reg('spacer', Ext.Spacer);/**
17897  * @class Ext.SplitBar
17898  * @extends Ext.util.Observable
17899  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
17900  * <br><br>
17901  * Usage:
17902  * <pre><code>
17903 var split = new Ext.SplitBar("elementToDrag", "elementToSize",
17904                    Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);
17905 split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));
17906 split.minSize = 100;
17907 split.maxSize = 600;
17908 split.animate = true;
17909 split.on('moved', splitterMoved);
17910 </code></pre>
17911  * @constructor
17912  * Create a new SplitBar
17913  * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.
17914  * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged
17915  * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
17916  * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or
17917                         Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
17918                         position of the SplitBar).
17919  */
17920 Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
17921
17922     /** @private */
17923     this.el = Ext.get(dragElement, true);
17924     this.el.dom.unselectable = "on";
17925     /** @private */
17926     this.resizingEl = Ext.get(resizingElement, true);
17927
17928     /**
17929      * @private
17930      * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
17931      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
17932      * @type Number
17933      */
17934     this.orientation = orientation || Ext.SplitBar.HORIZONTAL;
17935
17936     /**
17937      * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.
17938      * @type Number
17939      * @property tickSize
17940      */
17941     /**
17942      * The minimum size of the resizing element. (Defaults to 0)
17943      * @type Number
17944      */
17945     this.minSize = 0;
17946
17947     /**
17948      * The maximum size of the resizing element. (Defaults to 2000)
17949      * @type Number
17950      */
17951     this.maxSize = 2000;
17952
17953     /**
17954      * Whether to animate the transition to the new size
17955      * @type Boolean
17956      */
17957     this.animate = false;
17958
17959     /**
17960      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
17961      * @type Boolean
17962      */
17963     this.useShim = false;
17964
17965     /** @private */
17966     this.shim = null;
17967
17968     if(!existingProxy){
17969         /** @private */
17970         this.proxy = Ext.SplitBar.createProxy(this.orientation);
17971     }else{
17972         this.proxy = Ext.get(existingProxy).dom;
17973     }
17974     /** @private */
17975     this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
17976
17977     /** @private */
17978     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
17979
17980     /** @private */
17981     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
17982
17983     /** @private */
17984     this.dragSpecs = {};
17985
17986     /**
17987      * @private The adapter to use to positon and resize elements
17988      */
17989     this.adapter = new Ext.SplitBar.BasicLayoutAdapter();
17990     this.adapter.init(this);
17991
17992     if(this.orientation == Ext.SplitBar.HORIZONTAL){
17993         /** @private */
17994         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);
17995         this.el.addClass("x-splitbar-h");
17996     }else{
17997         /** @private */
17998         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);
17999         this.el.addClass("x-splitbar-v");
18000     }
18001
18002     this.addEvents(
18003         /**
18004          * @event resize
18005          * Fires when the splitter is moved (alias for {@link #moved})
18006          * @param {Ext.SplitBar} this
18007          * @param {Number} newSize the new width or height
18008          */
18009         "resize",
18010         /**
18011          * @event moved
18012          * Fires when the splitter is moved
18013          * @param {Ext.SplitBar} this
18014          * @param {Number} newSize the new width or height
18015          */
18016         "moved",
18017         /**
18018          * @event beforeresize
18019          * Fires before the splitter is dragged
18020          * @param {Ext.SplitBar} this
18021          */
18022         "beforeresize",
18023
18024         "beforeapply"
18025     );
18026
18027     Ext.SplitBar.superclass.constructor.call(this);
18028 };
18029
18030 Ext.extend(Ext.SplitBar, Ext.util.Observable, {
18031     onStartProxyDrag : function(x, y){
18032         this.fireEvent("beforeresize", this);
18033         this.overlay =  Ext.DomHelper.append(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
18034         this.overlay.unselectable();
18035         this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
18036         this.overlay.show();
18037         Ext.get(this.proxy).setDisplayed("block");
18038         var size = this.adapter.getElementSize(this);
18039         this.activeMinSize = this.getMinimumSize();
18040         this.activeMaxSize = this.getMaximumSize();
18041         var c1 = size - this.activeMinSize;
18042         var c2 = Math.max(this.activeMaxSize - size, 0);
18043         if(this.orientation == Ext.SplitBar.HORIZONTAL){
18044             this.dd.resetConstraints();
18045             this.dd.setXConstraint(
18046                 this.placement == Ext.SplitBar.LEFT ? c1 : c2,
18047                 this.placement == Ext.SplitBar.LEFT ? c2 : c1,
18048                 this.tickSize
18049             );
18050             this.dd.setYConstraint(0, 0);
18051         }else{
18052             this.dd.resetConstraints();
18053             this.dd.setXConstraint(0, 0);
18054             this.dd.setYConstraint(
18055                 this.placement == Ext.SplitBar.TOP ? c1 : c2,
18056                 this.placement == Ext.SplitBar.TOP ? c2 : c1,
18057                 this.tickSize
18058             );
18059          }
18060         this.dragSpecs.startSize = size;
18061         this.dragSpecs.startPoint = [x, y];
18062         Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
18063     },
18064
18065     /**
18066      * @private Called after the drag operation by the DDProxy
18067      */
18068     onEndProxyDrag : function(e){
18069         Ext.get(this.proxy).setDisplayed(false);
18070         var endPoint = Ext.lib.Event.getXY(e);
18071         if(this.overlay){
18072             Ext.destroy(this.overlay);
18073             delete this.overlay;
18074         }
18075         var newSize;
18076         if(this.orientation == Ext.SplitBar.HORIZONTAL){
18077             newSize = this.dragSpecs.startSize +
18078                 (this.placement == Ext.SplitBar.LEFT ?
18079                     endPoint[0] - this.dragSpecs.startPoint[0] :
18080                     this.dragSpecs.startPoint[0] - endPoint[0]
18081                 );
18082         }else{
18083             newSize = this.dragSpecs.startSize +
18084                 (this.placement == Ext.SplitBar.TOP ?
18085                     endPoint[1] - this.dragSpecs.startPoint[1] :
18086                     this.dragSpecs.startPoint[1] - endPoint[1]
18087                 );
18088         }
18089         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
18090         if(newSize != this.dragSpecs.startSize){
18091             if(this.fireEvent('beforeapply', this, newSize) !== false){
18092                 this.adapter.setElementSize(this, newSize);
18093                 this.fireEvent("moved", this, newSize);
18094                 this.fireEvent("resize", this, newSize);
18095             }
18096         }
18097     },
18098
18099     /**
18100      * Get the adapter this SplitBar uses
18101      * @return The adapter object
18102      */
18103     getAdapter : function(){
18104         return this.adapter;
18105     },
18106
18107     /**
18108      * Set the adapter this SplitBar uses
18109      * @param {Object} adapter A SplitBar adapter object
18110      */
18111     setAdapter : function(adapter){
18112         this.adapter = adapter;
18113         this.adapter.init(this);
18114     },
18115
18116     /**
18117      * Gets the minimum size for the resizing element
18118      * @return {Number} The minimum size
18119      */
18120     getMinimumSize : function(){
18121         return this.minSize;
18122     },
18123
18124     /**
18125      * Sets the minimum size for the resizing element
18126      * @param {Number} minSize The minimum size
18127      */
18128     setMinimumSize : function(minSize){
18129         this.minSize = minSize;
18130     },
18131
18132     /**
18133      * Gets the maximum size for the resizing element
18134      * @return {Number} The maximum size
18135      */
18136     getMaximumSize : function(){
18137         return this.maxSize;
18138     },
18139
18140     /**
18141      * Sets the maximum size for the resizing element
18142      * @param {Number} maxSize The maximum size
18143      */
18144     setMaximumSize : function(maxSize){
18145         this.maxSize = maxSize;
18146     },
18147
18148     /**
18149      * Sets the initialize size for the resizing element
18150      * @param {Number} size The initial size
18151      */
18152     setCurrentSize : function(size){
18153         var oldAnimate = this.animate;
18154         this.animate = false;
18155         this.adapter.setElementSize(this, size);
18156         this.animate = oldAnimate;
18157     },
18158
18159     /**
18160      * Destroy this splitbar.
18161      * @param {Boolean} removeEl True to remove the element
18162      */
18163     destroy : function(removeEl){
18164         Ext.destroy(this.shim, Ext.get(this.proxy));
18165         this.dd.unreg();
18166         if(removeEl){
18167             this.el.remove();
18168         }
18169         this.purgeListeners();
18170     }
18171 });
18172
18173 /**
18174  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
18175  */
18176 Ext.SplitBar.createProxy = function(dir){
18177     var proxy = new Ext.Element(document.createElement("div"));
18178     document.body.appendChild(proxy.dom);
18179     proxy.unselectable();
18180     var cls = 'x-splitbar-proxy';
18181     proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
18182     return proxy.dom;
18183 };
18184
18185 /**
18186  * @class Ext.SplitBar.BasicLayoutAdapter
18187  * Default Adapter. It assumes the splitter and resizing element are not positioned
18188  * elements and only gets/sets the width of the element. Generally used for table based layouts.
18189  */
18190 Ext.SplitBar.BasicLayoutAdapter = function(){
18191 };
18192
18193 Ext.SplitBar.BasicLayoutAdapter.prototype = {
18194     // do nothing for now
18195     init : function(s){
18196
18197     },
18198     /**
18199      * Called before drag operations to get the current size of the resizing element.
18200      * @param {Ext.SplitBar} s The SplitBar using this adapter
18201      */
18202      getElementSize : function(s){
18203         if(s.orientation == Ext.SplitBar.HORIZONTAL){
18204             return s.resizingEl.getWidth();
18205         }else{
18206             return s.resizingEl.getHeight();
18207         }
18208     },
18209
18210     /**
18211      * Called after drag operations to set the size of the resizing element.
18212      * @param {Ext.SplitBar} s The SplitBar using this adapter
18213      * @param {Number} newSize The new size to set
18214      * @param {Function} onComplete A function to be invoked when resizing is complete
18215      */
18216     setElementSize : function(s, newSize, onComplete){
18217         if(s.orientation == Ext.SplitBar.HORIZONTAL){
18218             if(!s.animate){
18219                 s.resizingEl.setWidth(newSize);
18220                 if(onComplete){
18221                     onComplete(s, newSize);
18222                 }
18223             }else{
18224                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
18225             }
18226         }else{
18227
18228             if(!s.animate){
18229                 s.resizingEl.setHeight(newSize);
18230                 if(onComplete){
18231                     onComplete(s, newSize);
18232                 }
18233             }else{
18234                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
18235             }
18236         }
18237     }
18238 };
18239
18240 /**
18241  *@class Ext.SplitBar.AbsoluteLayoutAdapter
18242  * @extends Ext.SplitBar.BasicLayoutAdapter
18243  * Adapter that  moves the splitter element to align with the resized sizing element.
18244  * Used with an absolute positioned SplitBar.
18245  * @param {Mixed} container The container that wraps around the absolute positioned content. If it's
18246  * document.body, make sure you assign an id to the body element.
18247  */
18248 Ext.SplitBar.AbsoluteLayoutAdapter = function(container){
18249     this.basic = new Ext.SplitBar.BasicLayoutAdapter();
18250     this.container = Ext.get(container);
18251 };
18252
18253 Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {
18254     init : function(s){
18255         this.basic.init(s);
18256     },
18257
18258     getElementSize : function(s){
18259         return this.basic.getElementSize(s);
18260     },
18261
18262     setElementSize : function(s, newSize, onComplete){
18263         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
18264     },
18265
18266     moveSplitter : function(s){
18267         var yes = Ext.SplitBar;
18268         switch(s.placement){
18269             case yes.LEFT:
18270                 s.el.setX(s.resizingEl.getRight());
18271                 break;
18272             case yes.RIGHT:
18273                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
18274                 break;
18275             case yes.TOP:
18276                 s.el.setY(s.resizingEl.getBottom());
18277                 break;
18278             case yes.BOTTOM:
18279                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
18280                 break;
18281         }
18282     }
18283 };
18284
18285 /**
18286  * Orientation constant - Create a vertical SplitBar
18287  * @static
18288  * @type Number
18289  */
18290 Ext.SplitBar.VERTICAL = 1;
18291
18292 /**
18293  * Orientation constant - Create a horizontal SplitBar
18294  * @static
18295  * @type Number
18296  */
18297 Ext.SplitBar.HORIZONTAL = 2;
18298
18299 /**
18300  * Placement constant - The resizing element is to the left of the splitter element
18301  * @static
18302  * @type Number
18303  */
18304 Ext.SplitBar.LEFT = 1;
18305
18306 /**
18307  * Placement constant - The resizing element is to the right of the splitter element
18308  * @static
18309  * @type Number
18310  */
18311 Ext.SplitBar.RIGHT = 2;
18312
18313 /**
18314  * Placement constant - The resizing element is positioned above the splitter element
18315  * @static
18316  * @type Number
18317  */
18318 Ext.SplitBar.TOP = 3;
18319
18320 /**
18321  * Placement constant - The resizing element is positioned under splitter element
18322  * @static
18323  * @type Number
18324  */
18325 Ext.SplitBar.BOTTOM = 4;
18326 /**
18327  * @class Ext.Container
18328  * @extends Ext.BoxComponent
18329  * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
18330  * basic behavior of containing items, namely adding, inserting and removing items.</p>
18331  *
18332  * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
18333  * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
18334  * Container to be encapsulated by an HTML element to your specifications by using the
18335  * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option. This is a useful technique when creating
18336  * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
18337  * for example.</p>
18338  *
18339  * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
18340  * create one using the <b><code>'container'</code></b> xtype:<pre><code>
18341 // explicitly create a Container
18342 var embeddedColumns = new Ext.Container({
18343     autoEl: 'div',  // This is the default
18344     layout: 'column',
18345     defaults: {
18346         // implicitly create Container by specifying xtype
18347         xtype: 'container',
18348         autoEl: 'div', // This is the default.
18349         layout: 'form',
18350         columnWidth: 0.5,
18351         style: {
18352             padding: '10px'
18353         }
18354     },
18355 //  The two items below will be Ext.Containers, each encapsulated by a &lt;DIV> element.
18356     items: [{
18357         items: {
18358             xtype: 'datefield',
18359             name: 'startDate',
18360             fieldLabel: 'Start date'
18361         }
18362     }, {
18363         items: {
18364             xtype: 'datefield',
18365             name: 'endDate',
18366             fieldLabel: 'End date'
18367         }
18368     }]
18369 });</code></pre></p>
18370  *
18371  * <p><u><b>Layout</b></u></p>
18372  * <p>Container classes delegate the rendering of child Components to a layout
18373  * manager class which must be configured into the Container using the
18374  * <code><b>{@link #layout}</b></code> configuration property.</p>
18375  * <p>When either specifying child <code>{@link #items}</code> of a Container,
18376  * or dynamically {@link #add adding} Components to a Container, remember to
18377  * consider how you wish the Container to arrange those child elements, and
18378  * whether those child elements need to be sized using one of Ext's built-in
18379  * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
18380  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
18381  * renders child components, appending them one after the other inside the
18382  * Container, and <b>does not apply any sizing</b> at all.</p>
18383  * <p>A common mistake is when a developer neglects to specify a
18384  * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
18385  * TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
18386  * has been specified). If a Container is left to use the default
18387  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its
18388  * child components will be resized, or changed in any way when the Container
18389  * is resized.</p>
18390  * <p>Certain layout managers allow dynamic addition of child components.
18391  * Those that do include {@link Ext.layout.CardLayout},
18392  * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and
18393  * {@link Ext.layout.TableLayout}. For example:<pre><code>
18394 //  Create the GridPanel.
18395 var myNewGrid = new Ext.grid.GridPanel({
18396     store: myStore,
18397     columns: myColumnModel,
18398     title: 'Results', // the title becomes the title of the tab
18399 });
18400
18401 myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
18402 myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
18403  * </code></pre></p>
18404  * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
18405  * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which
18406  * means all its child items are sized to {@link Ext.layout.FitLayout fit}
18407  * exactly into its client area.
18408  * <p><b><u>Overnesting is a common problem</u></b>.
18409  * An example of overnesting occurs when a GridPanel is added to a TabPanel
18410  * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
18411  * <code><b>{@link #layout}</b></code> specified) and then add that wrapping Panel
18412  * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
18413  * Component which can be added directly to a Container. If the wrapping Panel
18414  * has no <code><b>{@link #layout}</b></code> configuration, then the overnested
18415  * GridPanel will not be sized as expected.<p>
18416  *
18417  * <p><u><b>Adding via remote configuration</b></u></p>
18418  *
18419  * <p>A server side script can be used to add Components which are generated dynamically on the server.
18420  * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
18421  * based on certain parameters:
18422  * </p><pre><code>
18423 // execute an Ajax request to invoke server side script:
18424 Ext.Ajax.request({
18425     url: 'gen-invoice-grid.php',
18426     // send additional parameters to instruct server script
18427     params: {
18428         startDate: Ext.getCmp('start-date').getValue(),
18429         endDate: Ext.getCmp('end-date').getValue()
18430     },
18431     // process the response object to add it to the TabPanel:
18432     success: function(xhr) {
18433         var newComponent = eval(xhr.responseText); // see discussion below
18434         myTabPanel.add(newComponent); // add the component to the TabPanel
18435         myTabPanel.setActiveTab(newComponent);
18436     },
18437     failure: function() {
18438         Ext.Msg.alert("Grid create failed", "Server communication failure");
18439     }
18440 });
18441 </code></pre>
18442  * <p>The server script needs to return an executable Javascript statement which, when processed
18443  * using <code>eval()</code>, will return either a config object with an {@link Ext.Component#xtype xtype},
18444  * or an instantiated Component. The server might return this for example:</p><pre><code>
18445 (function() {
18446     function formatDate(value){
18447         return value ? value.dateFormat('M d, Y') : '';
18448     };
18449
18450     var store = new Ext.data.Store({
18451         url: 'get-invoice-data.php',
18452         baseParams: {
18453             startDate: '01/01/2008',
18454             endDate: '01/31/2008'
18455         },
18456         reader: new Ext.data.JsonReader({
18457             record: 'transaction',
18458             idProperty: 'id',
18459             totalRecords: 'total'
18460         }, [
18461            'customer',
18462            'invNo',
18463            {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
18464            {name: 'value', type: 'float'}
18465         ])
18466     });
18467
18468     var grid = new Ext.grid.GridPanel({
18469         title: 'Invoice Report',
18470         bbar: new Ext.PagingToolbar(store),
18471         store: store,
18472         columns: [
18473             {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
18474             {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
18475             {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
18476             {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
18477         ],
18478     });
18479     store.load();
18480     return grid;  // return instantiated component
18481 })();
18482 </code></pre>
18483  * <p>When the above code fragment is passed through the <code>eval</code> function in the success handler
18484  * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function
18485  * runs, and returns the instantiated grid component.</p>
18486  * <p>Note: since the code above is <i>generated</i> by a server script, the <code>baseParams</code> for
18487  * the Store, the metadata to allow generation of the Record layout, and the ColumnModel
18488  * can all be generated into the code since these are all known on the server.</p>
18489  *
18490  * @xtype container
18491  */
18492 Ext.Container = Ext.extend(Ext.BoxComponent, {
18493     /**
18494      * @cfg {Boolean} monitorResize
18495      * True to automatically monitor window resize events to handle anything that is sensitive to the current size
18496      * of the viewport.  This value is typically managed by the chosen <code>{@link #layout}</code> and should not need
18497      * to be set manually.
18498      */
18499     /**
18500      * @cfg {String/Object} layout
18501      * <p><b>*Important</b>: In order for child items to be correctly sized and
18502      * positioned, typically a layout manager <b>must</b> be specified through
18503      * the <code>layout</code> configuration option.</p>
18504      * <br><p>The sizing and positioning of child {@link items} is the responsibility of
18505      * the Container's layout manager which creates and manages the type of layout
18506      * you have in mind.  For example:</p><pre><code>
18507 new Ext.Window({
18508     width:300, height: 300,
18509     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
18510     items: [{
18511         title: 'Panel inside a Window'
18512     }]
18513 }).show();
18514      * </code></pre>
18515      * <p>If the {@link #layout} configuration is not explicitly specified for
18516      * a general purpose container (e.g. Container or Panel) the
18517      * {@link Ext.layout.ContainerLayout default layout manager} will be used
18518      * which does nothing but render child components sequentially into the
18519      * Container (no sizing or positioning will be performed in this situation).
18520      * Some container classes implicitly specify a default layout
18521      * (e.g. FormPanel specifies <code>layout:'form'</code>). Other specific
18522      * purpose classes internally specify/manage their internal layout (e.g.
18523      * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).</p>
18524      * <br><p><b><code>layout</code></b> may be specified as either as an Object or
18525      * as a String:</p><div><ul class="mdetail-params">
18526      *
18527      * <li><u>Specify as an Object</u></li>
18528      * <div><ul class="mdetail-params">
18529      * <li>Example usage:</li>
18530 <pre><code>
18531 layout: {
18532     type: 'vbox',
18533     padding: '5',
18534     align: 'left'
18535 }
18536 </code></pre>
18537      *
18538      * <li><code><b>type</b></code></li>
18539      * <br/><p>The layout type to be used for this container.  If not specified,
18540      * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>
18541      * <br/><p>Valid layout <code>type</code> values are:</p>
18542      * <div class="sub-desc"><ul class="mdetail-params">
18543      * <li><code><b>{@link Ext.layout.AbsoluteLayout absolute}</b></code></li>
18544      * <li><code><b>{@link Ext.layout.AccordionLayout accordion}</b></code></li>
18545      * <li><code><b>{@link Ext.layout.AnchorLayout anchor}</b></code></li>
18546      * <li><code><b>{@link Ext.layout.ContainerLayout auto}</b></code> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
18547      * <li><code><b>{@link Ext.layout.BorderLayout border}</b></code></li>
18548      * <li><code><b>{@link Ext.layout.CardLayout card}</b></code></li>
18549      * <li><code><b>{@link Ext.layout.ColumnLayout column}</b></code></li>
18550      * <li><code><b>{@link Ext.layout.FitLayout fit}</b></code></li>
18551      * <li><code><b>{@link Ext.layout.FormLayout form}</b></code></li>
18552      * <li><code><b>{@link Ext.layout.HBoxLayout hbox}</b></code></li>
18553      * <li><code><b>{@link Ext.layout.MenuLayout menu}</b></code></li>
18554      * <li><code><b>{@link Ext.layout.TableLayout table}</b></code></li>
18555      * <li><code><b>{@link Ext.layout.ToolbarLayout toolbar}</b></code></li>
18556      * <li><code><b>{@link Ext.layout.VBoxLayout vbox}</b></code></li>
18557      * </ul></div>
18558      *
18559      * <li>Layout specific configuration properties</li>
18560      * <br/><p>Additional layout specific configuration properties may also be
18561      * specified. For complete details regarding the valid config options for
18562      * each layout type, see the layout class corresponding to the <code>type</code>
18563      * specified.</p>
18564      *
18565      * </ul></div>
18566      *
18567      * <li><u>Specify as a String</u></li>
18568      * <div><ul class="mdetail-params">
18569      * <li>Example usage:</li>
18570 <pre><code>
18571 layout: 'vbox',
18572 layoutConfig: {
18573     padding: '5',
18574     align: 'left'
18575 }
18576 </code></pre>
18577      * <li><code><b>layout</b></code></li>
18578      * <br/><p>The layout <code>type</code> to be used for this container (see list
18579      * of valid layout type values above).</p><br/>
18580      * <li><code><b>{@link #layoutConfig}</b></code></li>
18581      * <br/><p>Additional layout specific configuration properties. For complete
18582      * details regarding the valid config options for each layout type, see the
18583      * layout class corresponding to the <code>layout</code> specified.</p>
18584      * </ul></div></ul></div>
18585      */
18586     /**
18587      * @cfg {Object} layoutConfig
18588      * This is a config object containing properties specific to the chosen
18589      * <b><code>{@link #layout}</code></b> if <b><code>{@link #layout}</code></b>
18590      * has been specified as a <i>string</i>.</p>
18591      */
18592     /**
18593      * @cfg {Boolean/Number} bufferResize
18594      * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer
18595      * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
18596      * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to <code>50</code>.
18597      */
18598     bufferResize: 50,
18599
18600     /**
18601      * @cfg {String/Number} activeItem
18602      * A string component id or the numeric index of the component that should be initially activated within the
18603      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
18604      * item in the container's collection).  activeItem only applies to layout styles that can display
18605      * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and
18606      * {@link Ext.layout.FitLayout}).  Related to {@link Ext.layout.ContainerLayout#activeItem}.
18607      */
18608     /**
18609      * @cfg {Object/Array} items
18610      * <pre><b>** IMPORTANT</b>: be sure to <b>{@link #layout specify a <code>layout</code>} if needed ! **</b></pre>
18611      * <p>A single item, or an array of child Components to be added to this container,
18612      * for example:</p>
18613      * <pre><code>
18614 // specifying a single item
18615 items: {...},
18616 layout: 'fit',    // specify a layout!
18617
18618 // specifying multiple items
18619 items: [{...}, {...}],
18620 layout: 'anchor', // specify a layout!
18621      * </code></pre>
18622      * <p>Each item may be:</p>
18623      * <div><ul class="mdetail-params">
18624      * <li>any type of object based on {@link Ext.Component}</li>
18625      * <li>a fully instanciated object or</li>
18626      * <li>an object literal that:</li>
18627      * <div><ul class="mdetail-params">
18628      * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>
18629      * <li>the {@link Ext.Component#xtype} specified is associated with the Component
18630      * desired and should be chosen from one of the available xtypes as listed
18631      * in {@link Ext.Component}.</li>
18632      * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly
18633      * specified, the {@link #defaultType} for that Container is used.</li>
18634      * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully
18635      * instanciated Component object</li>
18636      * </ul></div></ul></div>
18637      * <p><b>Notes</b>:</p>
18638      * <div><ul class="mdetail-params">
18639      * <li>Ext uses lazy rendering. Child Components will only be rendered
18640      * should it become necessary. Items are automatically laid out when they are first
18641      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>
18642      * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/
18643      * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>
18644      * </ul></div>
18645      */
18646     /**
18647      * @cfg {Object|Function} defaults
18648      * <p>This option is a means of applying default settings to all added items whether added through the {@link #items}
18649      * config or via the {@link #add} or {@link #insert} methods.</p>
18650      * <p>If an added item is a config object, and <b>not</b> an instantiated Component, then the default properties are
18651      * unconditionally applied. If the added item <b>is</b> an instantiated Component, then the default properties are
18652      * applied conditionally so as not to override existing properties in the item.</p>
18653      * <p>If the defaults option is specified as a function, then the function will be called using this Container as the
18654      * scope (<code>this</code> reference) and passing the added item as the first parameter. Any resulting object
18655      * from that call is then applied to the item as default properties.</p>
18656      * <p>For example, to automatically apply padding to the body of each of a set of
18657      * contained {@link Ext.Panel} items, you could pass: <code>defaults: {bodyStyle:'padding:15px'}</code>.</p>
18658      * <p>Usage:</p><pre><code>
18659 defaults: {               // defaults are applied to items, not the container
18660     autoScroll:true
18661 },
18662 items: [
18663     {
18664         xtype: 'panel',   // defaults <b>do not</b> have precedence over
18665         id: 'panel1',     // options in config objects, so the defaults
18666         autoScroll: false // will not be applied here, panel1 will be autoScroll:false
18667     },
18668     new Ext.Panel({       // defaults <b>do</b> have precedence over options
18669         id: 'panel2',     // options in components, so the defaults
18670         autoScroll: false // will be applied here, panel2 will be autoScroll:true.
18671     })
18672 ]
18673      * </code></pre>
18674      */
18675
18676
18677     /** @cfg {Boolean} autoDestroy
18678      * If true the container will automatically destroy any contained component that is removed from it, else
18679      * destruction must be handled manually (defaults to true).
18680      */
18681     autoDestroy : true,
18682
18683     /** @cfg {Boolean} forceLayout
18684      * If true the container will force a layout initially even if hidden or collapsed. This option
18685      * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false).
18686      */
18687     forceLayout: false,
18688
18689     /** @cfg {Boolean} hideBorders
18690      * True to hide the borders of each contained component, false to defer to the component's existing
18691      * border settings (defaults to false).
18692      */
18693     /** @cfg {String} defaultType
18694      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
18695      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
18696      * <p>Defaults to <code>'panel'</code>, except {@link Ext.menu.Menu} which defaults to <code>'menuitem'</code>,
18697      * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to <code>'button'</code>.</p>
18698      */
18699     defaultType : 'panel',
18700
18701     /** @cfg {String} resizeEvent
18702      * The event to listen to for resizing in layouts. Defaults to <code>'resize'</code>.
18703      */
18704     resizeEvent: 'resize',
18705
18706     /**
18707      * @cfg {Array} bubbleEvents
18708      * <p>An array of events that, when fired, should be bubbled to any parent container.
18709      * See {@link Ext.util.Observable#enableBubble}.
18710      * Defaults to <code>['add', 'remove']</code>.
18711      */
18712     bubbleEvents: ['add', 'remove'],
18713
18714     // private
18715     initComponent : function(){
18716         Ext.Container.superclass.initComponent.call(this);
18717
18718         this.addEvents(
18719             /**
18720              * @event afterlayout
18721              * Fires when the components in this container are arranged by the associated layout manager.
18722              * @param {Ext.Container} this
18723              * @param {ContainerLayout} layout The ContainerLayout implementation for this container
18724              */
18725             'afterlayout',
18726             /**
18727              * @event beforeadd
18728              * Fires before any {@link Ext.Component} is added or inserted into the container.
18729              * A handler can return false to cancel the add.
18730              * @param {Ext.Container} this
18731              * @param {Ext.Component} component The component being added
18732              * @param {Number} index The index at which the component will be added to the container's items collection
18733              */
18734             'beforeadd',
18735             /**
18736              * @event beforeremove
18737              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
18738              * false to cancel the remove.
18739              * @param {Ext.Container} this
18740              * @param {Ext.Component} component The component being removed
18741              */
18742             'beforeremove',
18743             /**
18744              * @event add
18745              * @bubbles
18746              * Fires after any {@link Ext.Component} is added or inserted into the container.
18747              * @param {Ext.Container} this
18748              * @param {Ext.Component} component The component that was added
18749              * @param {Number} index The index at which the component was added to the container's items collection
18750              */
18751             'add',
18752             /**
18753              * @event remove
18754              * @bubbles
18755              * Fires after any {@link Ext.Component} is removed from the container.
18756              * @param {Ext.Container} this
18757              * @param {Ext.Component} component The component that was removed
18758              */
18759             'remove'
18760         );
18761
18762         /**
18763          * The collection of components in this container as a {@link Ext.util.MixedCollection}
18764          * @type MixedCollection
18765          * @property items
18766          */
18767         var items = this.items;
18768         if(items){
18769             delete this.items;
18770             this.add(items);
18771         }
18772     },
18773
18774     // private
18775     initItems : function(){
18776         if(!this.items){
18777             this.items = new Ext.util.MixedCollection(false, this.getComponentId);
18778             this.getLayout(); // initialize the layout
18779         }
18780     },
18781
18782     // private
18783     setLayout : function(layout){
18784         if(this.layout && this.layout != layout){
18785             this.layout.setContainer(null);
18786         }
18787         this.initItems();
18788         this.layout = layout;
18789         layout.setContainer(this);
18790     },
18791
18792     afterRender: function(){
18793         // Render this Container, this should be done before setLayout is called which
18794         // will hook onResize
18795         Ext.Container.superclass.afterRender.call(this);
18796         if(!this.layout){
18797             this.layout = 'auto';
18798         }
18799         if(Ext.isObject(this.layout) && !this.layout.layout){
18800             this.layoutConfig = this.layout;
18801             this.layout = this.layoutConfig.type;
18802         }
18803         if(Ext.isString(this.layout)){
18804             this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
18805         }
18806         this.setLayout(this.layout);
18807
18808         // If a CardLayout, the active item set
18809         if(this.activeItem !== undefined){
18810             var item = this.activeItem;
18811             delete this.activeItem;
18812             this.layout.setActiveItem(item);
18813         }
18814
18815         // If we have no ownerCt, render and size all children
18816         if(!this.ownerCt){
18817             this.doLayout(false, true);
18818         }
18819
18820         // This is a manually configured flag set by users in conjunction with renderTo.
18821         // Not to be confused with the flag by the same name used in Layouts.
18822         if(this.monitorResize === true){
18823             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
18824         }
18825     },
18826
18827     /**
18828      * <p>Returns the Element to be used to contain the child Components of this Container.</p>
18829      * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but
18830      * if there is a more complex structure to a Container, this may be overridden to return
18831      * the element into which the {@link #layout layout} renders child Components.</p>
18832      * @return {Ext.Element} The Element to render child Components into.
18833      */
18834     getLayoutTarget : function(){
18835         return this.el;
18836     },
18837
18838     // private - used as the key lookup function for the items collection
18839     getComponentId : function(comp){
18840         return comp.getItemId();
18841     },
18842
18843     /**
18844      * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>
18845      * <br><p><b>Description</b></u> :
18846      * <div><ul class="mdetail-params">
18847      * <li>Fires the {@link #beforeadd} event before adding</li>
18848      * <li>The Container's {@link #defaults default config values} will be applied
18849      * accordingly (see <code>{@link #defaults}</code> for details).</li>
18850      * <li>Fires the {@link #add} event after the component has been added.</li>
18851      * </ul></div>
18852      * <br><p><b>Notes</b></u> :
18853      * <div><ul class="mdetail-params">
18854      * <li>If the Container is <i>already rendered</i> when <code>add</code>
18855      * is called, you may need to call {@link #doLayout} to refresh the view which causes
18856      * any unrendered child Components to be rendered. This is required so that you can
18857      * <code>add</code> multiple child components if needed while only refreshing the layout
18858      * once. For example:<pre><code>
18859 var tb = new {@link Ext.Toolbar}();
18860 tb.render(document.body);  // toolbar is rendered
18861 tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
18862 tb.add({text:'Button 2'});
18863 tb.{@link #doLayout}();             // refresh the layout
18864      * </code></pre></li>
18865      * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager
18866      * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
18867      * for more details.</li>
18868      * </ul></div>
18869      * @param {...Object/Array} component
18870      * <p>Either one or more Components to add or an Array of Components to add.  See
18871      * <code>{@link #items}</code> for additional information.</p>
18872      * @return {Ext.Component/Array} The Components that were added.
18873      */
18874     add : function(comp){
18875         this.initItems();
18876         var args = arguments.length > 1;
18877         if(args || Ext.isArray(comp)){
18878             var result = [];
18879             Ext.each(args ? arguments : comp, function(c){
18880                 result.push(this.add(c));
18881             }, this);
18882             return result;
18883         }
18884         var c = this.lookupComponent(this.applyDefaults(comp));
18885         var index = this.items.length;
18886         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
18887             this.items.add(c);
18888             // *onAdded
18889             c.onAdded(this, index);
18890             this.onAdd(c);
18891             this.fireEvent('add', this, c, index);
18892         }
18893         return c;
18894     },
18895
18896     onAdd : function(c){
18897         // Empty template method
18898     },
18899
18900     // private
18901     onAdded : function(container, pos) {
18902         //overridden here so we can cascade down, not worth creating a template method.
18903         this.ownerCt = container;
18904         this.initRef();
18905         //initialize references for child items
18906         this.cascade(function(c){
18907             c.initRef();
18908         });
18909         this.fireEvent('added', this, container, pos);
18910     },
18911
18912     /**
18913      * Inserts a Component into this Container at a specified index. Fires the
18914      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
18915      * Component has been inserted.
18916      * @param {Number} index The index at which the Component will be inserted
18917      * into the Container's items collection
18918      * @param {Ext.Component} component The child Component to insert.<br><br>
18919      * Ext uses lazy rendering, and will only render the inserted Component should
18920      * it become necessary.<br><br>
18921      * A Component config object may be passed in order to avoid the overhead of
18922      * constructing a real Component object if lazy rendering might mean that the
18923      * inserted Component will not be rendered immediately. To take advantage of
18924      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
18925      * property to the registered type of the Component wanted.<br><br>
18926      * For a list of all available xtypes, see {@link Ext.Component}.
18927      * @return {Ext.Component} component The Component (or config object) that was
18928      * inserted with the Container's default config values applied.
18929      */
18930     insert : function(index, comp){
18931         this.initItems();
18932         var a = arguments, len = a.length;
18933         if(len > 2){
18934             var result = [];
18935             for(var i = len-1; i >= 1; --i) {
18936                 result.push(this.insert(index, a[i]));
18937             }
18938             return result;
18939         }
18940         var c = this.lookupComponent(this.applyDefaults(comp));
18941         index = Math.min(index, this.items.length);
18942         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
18943             if(c.ownerCt == this){
18944                 this.items.remove(c);
18945             }
18946             this.items.insert(index, c);
18947             c.onAdded(this, index);
18948             this.onAdd(c);
18949             this.fireEvent('add', this, c, index);
18950         }
18951         return c;
18952     },
18953
18954     // private
18955     applyDefaults : function(c){
18956         var d = this.defaults;
18957         if(d){
18958             if(Ext.isFunction(d)){
18959                 d = d.call(this, c);
18960             }
18961             if(Ext.isString(c)){
18962                 c = Ext.ComponentMgr.get(c);
18963                 Ext.apply(c, d);
18964             }else if(!c.events){
18965                 Ext.applyIf(c, d);
18966             }else{
18967                 Ext.apply(c, d);
18968             }
18969         }
18970         return c;
18971     },
18972
18973     // private
18974     onBeforeAdd : function(item){
18975         if(item.ownerCt){
18976             item.ownerCt.remove(item, false);
18977         }
18978         if(this.hideBorders === true){
18979             item.border = (item.border === true);
18980         }
18981     },
18982
18983     /**
18984      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
18985      * the {@link #remove} event after the component has been removed.
18986      * @param {Component/String} component The component reference or id to remove.
18987      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
18988      * Defaults to the value of this Container's {@link #autoDestroy} config.
18989      * @return {Ext.Component} component The Component that was removed.
18990      */
18991     remove : function(comp, autoDestroy){
18992         this.initItems();
18993         var c = this.getComponent(comp);
18994         if(c && this.fireEvent('beforeremove', this, c) !== false){
18995             this.doRemove(c, autoDestroy);
18996             this.fireEvent('remove', this, c);
18997         }
18998         return c;
18999     },
19000
19001     onRemove: function(c){
19002         // Empty template method
19003     },
19004
19005     // private
19006     doRemove: function(c, autoDestroy){
19007         var l = this.layout,
19008             hasLayout = l && this.rendered;
19009
19010         if(hasLayout){
19011             l.onRemove(c);
19012         }
19013         this.items.remove(c);
19014         c.onRemoved();
19015         this.onRemove(c);
19016         if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
19017             c.destroy();
19018         }
19019         if(hasLayout){
19020             l.afterRemove(c);
19021         }
19022     },
19023
19024     /**
19025      * Removes all components from this container.
19026      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
19027      * Defaults to the value of this Container's {@link #autoDestroy} config.
19028      * @return {Array} Array of the destroyed components
19029      */
19030     removeAll: function(autoDestroy){
19031         this.initItems();
19032         var item, rem = [], items = [];
19033         this.items.each(function(i){
19034             rem.push(i);
19035         });
19036         for (var i = 0, len = rem.length; i < len; ++i){
19037             item = rem[i];
19038             this.remove(item, autoDestroy);
19039             if(item.ownerCt !== this){
19040                 items.push(item);
19041             }
19042         }
19043         return items;
19044     },
19045
19046     /**
19047      * Examines this container's <code>{@link #items}</code> <b>property</b>
19048      * and gets a direct child component of this container.
19049      * @param {String/Number} comp This parameter may be any of the following:
19050      * <div><ul class="mdetail-params">
19051      * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
19052      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
19053      * <li>a <b><code>Number</code></b> : representing the position of the child component
19054      * within the <code>{@link #items}</code> <b>property</b></li>
19055      * </ul></div>
19056      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
19057      * @return Ext.Component The component (if found).
19058      */
19059     getComponent : function(comp){
19060         if(Ext.isObject(comp)){
19061             comp = comp.getItemId();
19062         }
19063         return this.items.get(comp);
19064     },
19065
19066     // private
19067     lookupComponent : function(comp){
19068         if(Ext.isString(comp)){
19069             return Ext.ComponentMgr.get(comp);
19070         }else if(!comp.events){
19071             return this.createComponent(comp);
19072         }
19073         return comp;
19074     },
19075
19076     // private
19077     createComponent : function(config, defaultType){
19078         if (config.render) {
19079             return config;
19080         }
19081         // add in ownerCt at creation time but then immediately
19082         // remove so that onBeforeAdd can handle it
19083         var c = Ext.create(Ext.apply({
19084             ownerCt: this
19085         }, config), defaultType || this.defaultType);
19086         delete c.initialConfig.ownerCt;
19087         delete c.ownerCt;
19088         return c;
19089     },
19090
19091     /**
19092     * We can only lay out if there is a view area in which to layout.
19093     * display:none on the layout target, *or any of its parent elements* will mean it has no view area.
19094     */
19095
19096     // private
19097     canLayout : function() {
19098         var el = this.getVisibilityEl();
19099         return el && el.dom && !el.isStyle("display", "none");
19100     },
19101
19102     /**
19103      * Force this container's layout to be recalculated. A call to this function is required after adding a new component
19104      * to an already rendered container, or possibly after changing sizing/position properties of child components.
19105      * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto
19106      * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)
19107      * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden.
19108      * @return {Ext.Container} this
19109      */
19110
19111     doLayout : function(shallow, force){
19112         var rendered = this.rendered,
19113             forceLayout = force || this.forceLayout;
19114
19115         if(this.collapsed || !this.canLayout()){
19116             this.deferLayout = this.deferLayout || !shallow;
19117             if(!forceLayout){
19118                 return;
19119             }
19120             shallow = shallow && !this.deferLayout;
19121         } else {
19122             delete this.deferLayout;
19123         }
19124         if(rendered && this.layout){
19125             this.layout.layout();
19126         }
19127         if(shallow !== true && this.items){
19128             var cs = this.items.items;
19129             for(var i = 0, len = cs.length; i < len; i++){
19130                 var c = cs[i];
19131                 if(c.doLayout){
19132                     c.doLayout(false, forceLayout);
19133                 }
19134             }
19135         }
19136         if(rendered){
19137             this.onLayout(shallow, forceLayout);
19138         }
19139         // Initial layout completed
19140         this.hasLayout = true;
19141         delete this.forceLayout;
19142     },
19143
19144     onLayout : Ext.emptyFn,
19145
19146     // private
19147     shouldBufferLayout: function(){
19148         /*
19149          * Returns true if the container should buffer a layout.
19150          * This is true only if the container has previously been laid out
19151          * and has a parent container that is pending a layout.
19152          */
19153         var hl = this.hasLayout;
19154         if(this.ownerCt){
19155             // Only ever buffer if we've laid out the first time and we have one pending.
19156             return hl ? !this.hasLayoutPending() : false;
19157         }
19158         // Never buffer initial layout
19159         return hl;
19160     },
19161
19162     // private
19163     hasLayoutPending: function(){
19164         // Traverse hierarchy to see if any parent container has a pending layout.
19165         var pending = false;
19166         this.ownerCt.bubble(function(c){
19167             if(c.layoutPending){
19168                 pending = true;
19169                 return false;
19170             }
19171         });
19172         return pending;
19173     },
19174
19175     onShow : function(){
19176         // removes css classes that were added to hide
19177         Ext.Container.superclass.onShow.call(this);
19178         // If we were sized during the time we were hidden, layout.
19179         if(Ext.isDefined(this.deferLayout)){
19180             delete this.deferLayout;
19181             this.doLayout(true);
19182         }
19183     },
19184
19185     /**
19186      * Returns the layout currently in use by the container.  If the container does not currently have a layout
19187      * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
19188      * @return {ContainerLayout} layout The container's layout
19189      */
19190     getLayout : function(){
19191         if(!this.layout){
19192             var layout = new Ext.layout.AutoLayout(this.layoutConfig);
19193             this.setLayout(layout);
19194         }
19195         return this.layout;
19196     },
19197
19198     // private
19199     beforeDestroy : function(){
19200         var c;
19201         if(this.items){
19202             while(c = this.items.first()){
19203                 this.doRemove(c, true);
19204             }
19205         }
19206         if(this.monitorResize){
19207             Ext.EventManager.removeResizeListener(this.doLayout, this);
19208         }
19209         Ext.destroy(this.layout);
19210         Ext.Container.superclass.beforeDestroy.call(this);
19211     },
19212
19213     /**
19214      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
19215      * function call will be the scope provided or the current component. The arguments to the function
19216      * will be the args provided or the current component. If the function returns false at any point,
19217      * the bubble is stopped.
19218      * @param {Function} fn The function to call
19219      * @param {Object} scope (optional) The scope of the function (defaults to current node)
19220      * @param {Array} args (optional) The args to call the function with (default to passing the current component)
19221      * @return {Ext.Container} this
19222      */
19223     bubble : function(fn, scope, args){
19224         var p = this;
19225         while(p){
19226             if(fn.apply(scope || p, args || [p]) === false){
19227                 break;
19228             }
19229             p = p.ownerCt;
19230         }
19231         return this;
19232     },
19233
19234     /**
19235      * Cascades down the component/container heirarchy from this component (called first), calling the specified function with
19236      * each component. The scope (<i>this</i>) of
19237      * function call will be the scope provided or the current component. The arguments to the function
19238      * will be the args provided or the current component. If the function returns false at any point,
19239      * the cascade is stopped on that branch.
19240      * @param {Function} fn The function to call
19241      * @param {Object} scope (optional) The scope of the function (defaults to current component)
19242      * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)
19243      * @return {Ext.Container} this
19244      */
19245     cascade : function(fn, scope, args){
19246         if(fn.apply(scope || this, args || [this]) !== false){
19247             if(this.items){
19248                 var cs = this.items.items;
19249                 for(var i = 0, len = cs.length; i < len; i++){
19250                     if(cs[i].cascade){
19251                         cs[i].cascade(fn, scope, args);
19252                     }else{
19253                         fn.apply(scope || cs[i], args || [cs[i]]);
19254                     }
19255                 }
19256             }
19257         }
19258         return this;
19259     },
19260
19261     /**
19262      * Find a component under this container at any level by id
19263      * @param {String} id
19264      * @return Ext.Component
19265      */
19266     findById : function(id){
19267         var m, ct = this;
19268         this.cascade(function(c){
19269             if(ct != c && c.id === id){
19270                 m = c;
19271                 return false;
19272             }
19273         });
19274         return m || null;
19275     },
19276
19277     /**
19278      * Find a component under this container at any level by xtype or class
19279      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
19280      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
19281      * the default), or true to check whether this Component is directly of the specified xtype.
19282      * @return {Array} Array of Ext.Components
19283      */
19284     findByType : function(xtype, shallow){
19285         return this.findBy(function(c){
19286             return c.isXType(xtype, shallow);
19287         });
19288     },
19289
19290     /**
19291      * Find a component under this container at any level by property
19292      * @param {String} prop
19293      * @param {String} value
19294      * @return {Array} Array of Ext.Components
19295      */
19296     find : function(prop, value){
19297         return this.findBy(function(c){
19298             return c[prop] === value;
19299         });
19300     },
19301
19302     /**
19303      * Find a component under this container at any level by a custom function. If the passed function returns
19304      * true, the component will be included in the results. The passed function is called with the arguments (component, this container).
19305      * @param {Function} fn The function to call
19306      * @param {Object} scope (optional)
19307      * @return {Array} Array of Ext.Components
19308      */
19309     findBy : function(fn, scope){
19310         var m = [], ct = this;
19311         this.cascade(function(c){
19312             if(ct != c && fn.call(scope || c, c, ct) === true){
19313                 m.push(c);
19314             }
19315         });
19316         return m;
19317     },
19318
19319     /**
19320      * Get a component contained by this container (alias for items.get(key))
19321      * @param {String/Number} key The index or id of the component
19322      * @return {Ext.Component} Ext.Component
19323      */
19324     get : function(key){
19325         return this.items.get(key);
19326     }
19327 });
19328
19329 Ext.Container.LAYOUTS = {};
19330 Ext.reg('container', Ext.Container);
19331 /**
19332  * @class Ext.layout.ContainerLayout
19333  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
19334  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
19335  */
19336 Ext.layout.ContainerLayout = Ext.extend(Object, {
19337     /**
19338      * @cfg {String} extraCls
19339      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
19340      * customized styles to the container or any of its children using standard CSS rules. See
19341      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
19342      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
19343      * which assign a value by default:
19344      * <div class="mdetail-params"><ul>
19345      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
19346      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
19347      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
19348      * </ul></div>
19349      * To configure the above Classes with an extra CSS class append to the default.  For example,
19350      * for ColumnLayout:<pre><code>
19351      * extraCls: 'x-column custom-class'
19352      * </code></pre>
19353      * </p>
19354      */
19355     /**
19356      * @cfg {Boolean} renderHidden
19357      * True to hide each contained item on render (defaults to false).
19358      */
19359
19360     /**
19361      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
19362      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
19363      * </code></pre>
19364      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
19365      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
19366      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
19367      * @type {Ext.Component}
19368      * @property activeItem
19369      */
19370
19371     // private
19372     monitorResize:false,
19373     // private
19374     activeItem : null,
19375
19376     constructor : function(config){
19377         this.id = Ext.id(null, 'ext-layout-');
19378         Ext.apply(this, config);
19379     },
19380
19381     type: 'container',
19382
19383     /* Workaround for how IE measures autoWidth elements.  It prefers bottom-up measurements
19384       whereas other browser prefer top-down.  We will hide all target child elements before we measure and
19385       put them back to get an accurate measurement.
19386     */
19387     IEMeasureHack : function(target, viewFlag) {
19388         var tChildren = target.dom.childNodes, tLen = tChildren.length, c, d = [], e, i, ret;
19389         for (i = 0 ; i < tLen ; i++) {
19390             c = tChildren[i];
19391             e = Ext.get(c);
19392             if (e) {
19393                 d[i] = e.getStyle('display');
19394                 e.setStyle({display: 'none'});
19395             }
19396         }
19397         ret = target ? target.getViewSize(viewFlag) : {};
19398         for (i = 0 ; i < tLen ; i++) {
19399             c = tChildren[i];
19400             e = Ext.get(c);
19401             if (e) {
19402                 e.setStyle({display: d[i]});
19403             }
19404         }
19405         return ret;
19406     },
19407
19408     // Placeholder for the derived layouts
19409     getLayoutTargetSize : Ext.EmptyFn,
19410
19411     // private
19412     layout : function(){
19413         var ct = this.container, target = ct.getLayoutTarget();
19414         if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){
19415             target.addClass(this.targetCls);
19416         }
19417         this.onLayout(ct, target);
19418         ct.fireEvent('afterlayout', ct, this);
19419     },
19420
19421     // private
19422     onLayout : function(ct, target){
19423         this.renderAll(ct, target);
19424     },
19425
19426     // private
19427     isValidParent : function(c, target){
19428         return target && c.getPositionEl().dom.parentNode == (target.dom || target);
19429     },
19430
19431     // private
19432     renderAll : function(ct, target){
19433         var items = ct.items.items, i, c, len = items.length;
19434         for(i = 0; i < len; i++) {
19435             c = items[i];
19436             if(c && (!c.rendered || !this.isValidParent(c, target))){
19437                 this.renderItem(c, i, target);
19438             }
19439         }
19440     },
19441
19442     /**
19443      * @private
19444      * Renders the given Component into the target Element. If the Component is already rendered,
19445      * it is moved to the provided target instead.
19446      * @param {Ext.Component} c The Component to render
19447      * @param {Number} position The position within the target to render the item to
19448      * @param {Ext.Element} target The target Element
19449      */
19450     renderItem : function(c, position, target){
19451         if (c) {
19452             if (!c.rendered) {
19453                 c.render(target, position);
19454                 this.configureItem(c, position);
19455             } else if (!this.isValidParent(c, target)) {
19456                 if (Ext.isNumber(position)) {
19457                     position = target.dom.childNodes[position];
19458                 }
19459                 
19460                 target.dom.insertBefore(c.getPositionEl().dom, position || null);
19461                 c.container = target;
19462                 this.configureItem(c, position);
19463             }
19464         }
19465     },
19466
19467     // private.
19468     // Get all rendered items to lay out.
19469     getRenderedItems: function(ct){
19470         var t = ct.getLayoutTarget(), cti = ct.items.items, len = cti.length, i, c, items = [];
19471         for (i = 0; i < len; i++) {
19472             if((c = cti[i]).rendered && this.isValidParent(c, t)){
19473                 items.push(c);
19474             }
19475         };
19476         return items;
19477     },
19478
19479     /**
19480      * @private
19481      * Applies extraCls and hides the item if renderHidden is true
19482      */
19483     configureItem: function(c, position){
19484         if (this.extraCls) {
19485             var t = c.getPositionEl ? c.getPositionEl() : c;
19486             t.addClass(this.extraCls);
19487         }
19488         
19489         // If we are forcing a layout, do so *before* we hide so elements have height/width
19490         if (c.doLayout && this.forceLayout) {
19491             c.doLayout();
19492         }
19493         if (this.renderHidden && c != this.activeItem) {
19494             c.hide();
19495         }
19496     },
19497
19498     onRemove: function(c){
19499         if(this.activeItem == c){
19500             delete this.activeItem;
19501         }
19502         if(c.rendered && this.extraCls){
19503             var t = c.getPositionEl ? c.getPositionEl() : c;
19504             t.removeClass(this.extraCls);
19505         }
19506     },
19507
19508     afterRemove: function(c){
19509         if(c.removeRestore){
19510             c.removeMode = 'container';
19511             delete c.removeRestore;
19512         }
19513     },
19514
19515     // private
19516     onResize: function(){
19517         var ct = this.container,
19518             b;
19519         if(ct.collapsed){
19520             return;
19521         }
19522         if(b = ct.bufferResize && ct.shouldBufferLayout()){
19523             if(!this.resizeTask){
19524                 this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
19525                 this.resizeBuffer = Ext.isNumber(b) ? b : 50;
19526             }
19527             ct.layoutPending = true;
19528             this.resizeTask.delay(this.resizeBuffer);
19529         }else{
19530             this.runLayout();
19531         }
19532     },
19533
19534     runLayout: function(){
19535         var ct = this.container;
19536         this.layout();
19537         ct.onLayout();
19538         delete ct.layoutPending;
19539     },
19540
19541     // private
19542     setContainer : function(ct){
19543         /**
19544          * This monitorResize flag will be renamed soon as to avoid confusion
19545          * with the Container version which hooks onWindowResize to doLayout
19546          *
19547          * monitorResize flag in this context attaches the resize event between
19548          * a container and it's layout
19549          */
19550         if(this.monitorResize && ct != this.container){
19551             var old = this.container;
19552             if(old){
19553                 old.un(old.resizeEvent, this.onResize, this);
19554             }
19555             if(ct){
19556                 ct.on(ct.resizeEvent, this.onResize, this);
19557             }
19558         }
19559         this.container = ct;
19560     },
19561
19562     /**
19563      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
19564      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
19565      * @param {Number|String} v The encoded margins
19566      * @return {Object} An object with margin sizes for top, right, bottom and left
19567      */
19568     parseMargins : function(v){
19569         if (Ext.isNumber(v)) {
19570             v = v.toString();
19571         }
19572         var ms  = v.split(' '),
19573             len = ms.length;
19574             
19575         if (len == 1) {
19576             ms[1] = ms[2] = ms[3] = ms[0];
19577         } else if(len == 2) {
19578             ms[2] = ms[0];
19579             ms[3] = ms[1];
19580         } else if(len == 3) {
19581             ms[3] = ms[1];
19582         }
19583         
19584         return {
19585             top   :parseInt(ms[0], 10) || 0,
19586             right :parseInt(ms[1], 10) || 0,
19587             bottom:parseInt(ms[2], 10) || 0,
19588             left  :parseInt(ms[3], 10) || 0
19589         };
19590     },
19591
19592     /**
19593      * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as
19594      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
19595      * labeled and styled form Field. A default Template is supplied, but this may be
19596      * overriden to create custom field structures. The template processes values returned from
19597      * {@link Ext.layout.FormLayout#getTemplateArgs}.
19598      * @property fieldTpl
19599      * @type Ext.Template
19600      */
19601     fieldTpl: (function() {
19602         var t = new Ext.Template(
19603             '<div class="x-form-item {itemCls}" tabIndex="-1">',
19604                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
19605                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
19606                 '</div><div class="{clearCls}"></div>',
19607             '</div>'
19608         );
19609         t.disableFormats = true;
19610         return t.compile();
19611     })(),
19612
19613     /*
19614      * Destroys this layout. This is a template method that is empty by default, but should be implemented
19615      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
19616      * @protected
19617      */
19618     destroy : function(){
19619         // Stop any buffered layout tasks
19620         if(this.resizeTask && this.resizeTask.cancel){
19621             this.resizeTask.cancel();
19622         }
19623         if(!Ext.isEmpty(this.targetCls)){
19624             var target = this.container.getLayoutTarget();
19625             if(target){
19626                 target.removeClass(this.targetCls);
19627             }
19628         }
19629     }
19630 });/**
19631  * @class Ext.layout.AutoLayout
19632  * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Container} to
19633  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
19634  * a {@link Ext.Container Container}.</tt>.  AutoLayout provides only a passthrough of any layout calls
19635  * to any child containers.</p>
19636  */
19637 Ext.layout.AutoLayout = Ext.extend(Ext.layout.ContainerLayout, {
19638     type: 'auto',
19639
19640     monitorResize: true,
19641
19642     onLayout : function(ct, target){
19643         Ext.layout.AutoLayout.superclass.onLayout.call(this, ct, target);
19644         var cs = this.getRenderedItems(ct), len = cs.length, i, c;
19645         for(i = 0; i < len; i++){
19646             c = cs[i];
19647             if (c.doLayout){
19648                 // Shallow layout children
19649                 c.doLayout(true);
19650             }
19651         }
19652     }
19653 });
19654
19655 Ext.Container.LAYOUTS['auto'] = Ext.layout.AutoLayout;
19656 /**
19657  * @class Ext.layout.FitLayout
19658  * @extends Ext.layout.ContainerLayout
19659  * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
19660  * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}
19661  * config, and should generally not need to be created directly via the new keyword.</p>
19662  * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container
19663  * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has
19664  * multiple panels, only the first one will be displayed.  Example usage:</p>
19665  * <pre><code>
19666 var p = new Ext.Panel({
19667     title: 'Fit Layout',
19668     layout:'fit',
19669     items: {
19670         title: 'Inner Panel',
19671         html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',
19672         border: false
19673     }
19674 });
19675 </code></pre>
19676  */
19677 Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {
19678     // private
19679     monitorResize:true,
19680
19681     type: 'fit',
19682
19683     getLayoutTargetSize : function() {
19684         var target = this.container.getLayoutTarget();
19685         if (!target) {
19686             return {};
19687         }
19688         // Style Sized (scrollbars not included)
19689         return target.getStyleSize();
19690     },
19691
19692     // private
19693     onLayout : function(ct, target){
19694         Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);
19695         if(!ct.collapsed){
19696             this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());
19697         }
19698     },
19699
19700     // private
19701     setItemSize : function(item, size){
19702         if(item && size.height > 0){ // display none?
19703             item.setSize(size);
19704         }
19705     }
19706 });
19707 Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**
19708  * @class Ext.layout.CardLayout
19709  * @extends Ext.layout.FitLayout
19710  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
19711  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.
19712  * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,
19713  * and should generally not need to be created directly via the new keyword.</p>
19714  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,
19715  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of
19716  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,
19717  * so that functionality must be provided by the developer.</p>
19718  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added
19719  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a
19720  * common navigation routine -- for this example, the implementation of that routine has been ommitted since
19721  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a
19722  * completely different implementation.  For serious implementations, a better approach would be to extend
19723  * CardLayout to provide the custom functionality needed.  Example usage:</p>
19724  * <pre><code>
19725 var navHandler = function(direction){
19726     // This routine could contain business logic required to manage the navigation steps.
19727     // It would call setActiveItem as needed, manage navigation button state, handle any
19728     // branching logic that might be required, handle alternate actions like cancellation
19729     // or finalization, etc.  A complete wizard implementation could get pretty
19730     // sophisticated depending on the complexity required, and should probably be
19731     // done as a subclass of CardLayout in a real-world implementation.
19732 };
19733
19734 var card = new Ext.Panel({
19735     title: 'Example Wizard',
19736     layout:'card',
19737     activeItem: 0, // make sure the active item is set on the container config!
19738     bodyStyle: 'padding:15px',
19739     defaults: {
19740         // applied to each contained panel
19741         border:false
19742     },
19743     // just an example of one possible navigation scheme, using buttons
19744     bbar: [
19745         {
19746             id: 'move-prev',
19747             text: 'Back',
19748             handler: navHandler.createDelegate(this, [-1]),
19749             disabled: true
19750         },
19751         '->', // greedy spacer so that the buttons are aligned to each side
19752         {
19753             id: 'move-next',
19754             text: 'Next',
19755             handler: navHandler.createDelegate(this, [1])
19756         }
19757     ],
19758     // the panels (or "cards") within the layout
19759     items: [{
19760         id: 'card-0',
19761         html: '&lt;h1&gt;Welcome to the Wizard!&lt;/h1&gt;&lt;p&gt;Step 1 of 3&lt;/p&gt;'
19762     },{
19763         id: 'card-1',
19764         html: '&lt;p&gt;Step 2 of 3&lt;/p&gt;'
19765     },{
19766         id: 'card-2',
19767         html: '&lt;h1&gt;Congratulations!&lt;/h1&gt;&lt;p&gt;Step 3 of 3 - Complete&lt;/p&gt;'
19768     }]
19769 });
19770 </code></pre>
19771  */
19772 Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {
19773     /**
19774      * @cfg {Boolean} deferredRender
19775      * True to render each contained item at the time it becomes active, false to render all contained items
19776      * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or
19777      * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to
19778      * true might improve performance.
19779      */
19780     deferredRender : false,
19781
19782     /**
19783      * @cfg {Boolean} layoutOnCardChange
19784      * True to force a layout of the active item when the active card is changed. Defaults to false.
19785      */
19786     layoutOnCardChange : false,
19787
19788     /**
19789      * @cfg {Boolean} renderHidden @hide
19790      */
19791     // private
19792     renderHidden : true,
19793
19794     type: 'card',
19795
19796     /**
19797      * Sets the active (visible) item in the layout.
19798      * @param {String/Number} item The string component id or numeric index of the item to activate
19799      */
19800     setActiveItem : function(item){
19801         var ai = this.activeItem,
19802             ct = this.container;
19803         item = ct.getComponent(item);
19804
19805         // Is this a valid, different card?
19806         if(item && ai != item){
19807
19808             // Changing cards, hide the current one
19809             if(ai){
19810                 ai.hide();
19811                 if (ai.hidden !== true) {
19812                     return false;
19813                 }
19814                 ai.fireEvent('deactivate', ai);
19815             }
19816
19817             var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered);
19818
19819             // Change activeItem reference
19820             this.activeItem = item;
19821
19822             // The container is about to get a recursive layout, remove any deferLayout reference
19823             // because it will trigger a redundant layout.
19824             delete item.deferLayout;
19825
19826             // Show the new component
19827             item.show();
19828
19829             this.layout();
19830
19831             if(layout){
19832                 item.doLayout();
19833             }
19834             item.fireEvent('activate', item);
19835         }
19836     },
19837
19838     // private
19839     renderAll : function(ct, target){
19840         if(this.deferredRender){
19841             this.renderItem(this.activeItem, undefined, target);
19842         }else{
19843             Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);
19844         }
19845     }
19846 });
19847 Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;
19848 /**
19849  * @class Ext.layout.AnchorLayout
19850  * @extends Ext.layout.ContainerLayout
19851  * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
19852  * If the container is resized, all anchored items are automatically rerendered according to their
19853  * <b><tt>{@link #anchor}</tt></b> rules.</p>
19854  * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}
19855  * config, and should generally not need to be created directly via the new keyword.</p>
19856  * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
19857  * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
19858  * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
19859  * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
19860  * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
19861  * logic if necessary.  For example:</p>
19862  * <pre><code>
19863 var viewport = new Ext.Viewport({
19864     layout:'anchor',
19865     anchorSize: {width:800, height:600},
19866     items:[{
19867         title:'Item 1',
19868         html:'Content 1',
19869         width:800,
19870         anchor:'right 20%'
19871     },{
19872         title:'Item 2',
19873         html:'Content 2',
19874         width:300,
19875         anchor:'50% 30%'
19876     },{
19877         title:'Item 3',
19878         html:'Content 3',
19879         width:600,
19880         anchor:'-100 50%'
19881     }]
19882 });
19883  * </code></pre>
19884  */
19885 Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
19886     /**
19887      * @cfg {String} anchor
19888      * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
19889      * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
19890      *
19891      * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
19892      * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
19893      * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
19894      * The following types of anchor values are supported:<div class="mdetail-params"><ul>
19895      *
19896      * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
19897      * The first anchor is the percentage width that the item should take up within the container, and the
19898      * second is the percentage height.  For example:<pre><code>
19899 // two values specified
19900 anchor: '100% 50%' // render item complete width of the container and
19901                    // 1/2 height of the container
19902 // one value specified
19903 anchor: '100%'     // the width value; the height will default to auto
19904      * </code></pre></div></li>
19905      *
19906      * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">
19907      * This is a raw adjustment where the first anchor is the offset from the right edge of the container,
19908      * and the second is the offset from the bottom edge. For example:<pre><code>
19909 // two values specified
19910 anchor: '-50 -100' // render item the complete width of the container
19911                    // minus 50 pixels and
19912                    // the complete height minus 100 pixels.
19913 // one value specified
19914 anchor: '-50'      // anchor value is assumed to be the right offset value
19915                    // bottom offset will default to 0
19916      * </code></pre></div></li>
19917      *
19918      * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>
19919      * (or <tt>'b'</tt>).<div class="sub-desc">
19920      * Either the container must have a fixed size or an anchorSize config value defined at render time in
19921      * order for these to have any effect.</div></li>
19922      *
19923      * <li><b>Mixed</b> : <div class="sub-desc">
19924      * Anchor values can also be mixed as needed.  For example, to render the width offset from the container
19925      * right edge by 50 pixels and 75% of the container's height use:
19926      * <pre><code>
19927 anchor: '-50 75%'
19928      * </code></pre></div></li>
19929      *
19930      *
19931      * </ul></div>
19932      */
19933
19934     // private
19935     monitorResize : true,
19936
19937     type : 'anchor',
19938
19939     /**
19940      * @cfg {String} defaultAnchor
19941      *
19942      * default anchor for all child container items applied if no anchor or specific width is set on the child item.  Defaults to '100%'.
19943      *
19944      */
19945     defaultAnchor : '100%',
19946
19947     parseAnchorRE : /^(r|right|b|bottom)$/i,
19948
19949     getLayoutTargetSize : function() {
19950         var target = this.container.getLayoutTarget();
19951         if (!target) {
19952             return {};
19953         }
19954         // Style Sized (scrollbars not included)
19955         return target.getStyleSize();
19956     },
19957
19958     // private
19959     onLayout : function(ct, target){
19960         Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target);
19961         var size = this.getLayoutTargetSize();
19962
19963         var w = size.width, h = size.height;
19964
19965         if(w < 20 && h < 20){
19966             return;
19967         }
19968
19969         // find the container anchoring size
19970         var aw, ah;
19971         if(ct.anchorSize){
19972             if(typeof ct.anchorSize == 'number'){
19973                 aw = ct.anchorSize;
19974             }else{
19975                 aw = ct.anchorSize.width;
19976                 ah = ct.anchorSize.height;
19977             }
19978         }else{
19979             aw = ct.initialConfig.width;
19980             ah = ct.initialConfig.height;
19981         }
19982
19983         var cs = this.getRenderedItems(ct), len = cs.length, i, c, a, cw, ch, el, vs, boxes = [];
19984         for(i = 0; i < len; i++){
19985             c = cs[i];
19986             el = c.getPositionEl();
19987
19988             // If a child container item has no anchor and no specific width, set the child to the default anchor size
19989             if (!c.anchor && c.items && !Ext.isNumber(c.width) && !(Ext.isIE6 && Ext.isStrict)){
19990                 c.anchor = this.defaultAnchor;
19991             }
19992
19993             if(c.anchor){
19994                 a = c.anchorSpec;
19995                 if(!a){ // cache all anchor values
19996                     vs = c.anchor.split(' ');
19997                     c.anchorSpec = a = {
19998                         right: this.parseAnchor(vs[0], c.initialConfig.width, aw),
19999                         bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah)
20000                     };
20001                 }
20002                 cw = a.right ? this.adjustWidthAnchor(a.right(w) - el.getMargins('lr'), c) : undefined;
20003                 ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h) - el.getMargins('tb'), c) : undefined;
20004
20005                 if(cw || ch){
20006                     boxes.push({
20007                         comp: c,
20008                         width: cw || undefined,
20009                         height: ch || undefined
20010                     });
20011                 }
20012             }
20013         }
20014         for (i = 0, len = boxes.length; i < len; i++) {
20015             c = boxes[i];
20016             c.comp.setSize(c.width, c.height);
20017         }
20018     },
20019
20020     // private
20021     parseAnchor : function(a, start, cstart){
20022         if(a && a != 'none'){
20023             var last;
20024             // standard anchor
20025             if(this.parseAnchorRE.test(a)){
20026                 var diff = cstart - start;
20027                 return function(v){
20028                     if(v !== last){
20029                         last = v;
20030                         return v - diff;
20031                     }
20032                 }
20033             // percentage
20034             }else if(a.indexOf('%') != -1){
20035                 var ratio = parseFloat(a.replace('%', ''))*.01;
20036                 return function(v){
20037                     if(v !== last){
20038                         last = v;
20039                         return Math.floor(v*ratio);
20040                     }
20041                 }
20042             // simple offset adjustment
20043             }else{
20044                 a = parseInt(a, 10);
20045                 if(!isNaN(a)){
20046                     return function(v){
20047                         if(v !== last){
20048                             last = v;
20049                             return v + a;
20050                         }
20051                     }
20052                 }
20053             }
20054         }
20055         return false;
20056     },
20057
20058     // private
20059     adjustWidthAnchor : function(value, comp){
20060         return value;
20061     },
20062
20063     // private
20064     adjustHeightAnchor : function(value, comp){
20065         return value;
20066     }
20067
20068     /**
20069      * @property activeItem
20070      * @hide
20071      */
20072 });
20073 Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;
20074 /**
20075  * @class Ext.layout.ColumnLayout
20076  * @extends Ext.layout.ContainerLayout
20077  * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of
20078  * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.
20079  * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,
20080  * and should generally not need to be created directly via the new keyword.</p>
20081  * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
20082  * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The
20083  * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.
20084  * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>
20085  * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.
20086  * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and
20087  * less than 1 (e.g., .25).</p>
20088  * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the
20089  * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none
20090  * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second
20091  * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on
20092  * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space
20093  * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns
20094  * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your
20095  * layout may not render as expected.  Example usage:</p>
20096  * <pre><code>
20097 // All columns are percentages -- they must add up to 1
20098 var p = new Ext.Panel({
20099     title: 'Column Layout - Percentage Only',
20100     layout:'column',
20101     items: [{
20102         title: 'Column 1',
20103         columnWidth: .25
20104     },{
20105         title: 'Column 2',
20106         columnWidth: .6
20107     },{
20108         title: 'Column 3',
20109         columnWidth: .15
20110     }]
20111 });
20112
20113 // Mix of width and columnWidth -- all columnWidth values must add up
20114 // to 1. The first column will take up exactly 120px, and the last two
20115 // columns will fill the remaining container width.
20116 var p = new Ext.Panel({
20117     title: 'Column Layout - Mixed',
20118     layout:'column',
20119     items: [{
20120         title: 'Column 1',
20121         width: 120
20122     },{
20123         title: 'Column 2',
20124         columnWidth: .8
20125     },{
20126         title: 'Column 3',
20127         columnWidth: .2
20128     }]
20129 });
20130 </code></pre>
20131  */
20132 Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {
20133     // private
20134     monitorResize:true,
20135
20136     type: 'column',
20137
20138     extraCls: 'x-column',
20139
20140     scrollOffset : 0,
20141
20142     // private
20143
20144     targetCls: 'x-column-layout-ct',
20145
20146     isValidParent : function(c, target){
20147         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
20148     },
20149
20150     getLayoutTargetSize : function() {
20151         var target = this.container.getLayoutTarget(), ret;
20152         if (target) {
20153             ret = target.getViewSize();
20154
20155             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
20156             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
20157             // with getViewSize
20158             if (Ext.isIE && Ext.isStrict && ret.width == 0){
20159                 ret =  target.getStyleSize();
20160             }
20161
20162             ret.width -= target.getPadding('lr');
20163             ret.height -= target.getPadding('tb');
20164         }
20165         return ret;
20166     },
20167
20168     renderAll : function(ct, target) {
20169         if(!this.innerCt){
20170             // the innerCt prevents wrapping and shuffling while
20171             // the container is resizing
20172             this.innerCt = target.createChild({cls:'x-column-inner'});
20173             this.innerCt.createChild({cls:'x-clear'});
20174         }
20175         Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt);
20176     },
20177
20178     // private
20179     onLayout : function(ct, target){
20180         var cs = ct.items.items,
20181             len = cs.length,
20182             c,
20183             i,
20184             m,
20185             margins = [];
20186
20187         this.renderAll(ct, target);
20188
20189         var size = this.getLayoutTargetSize();
20190
20191         if(size.width < 1 && size.height < 1){ // display none?
20192             return;
20193         }
20194
20195         var w = size.width - this.scrollOffset,
20196             h = size.height,
20197             pw = w;
20198
20199         this.innerCt.setWidth(w);
20200
20201         // some columns can be percentages while others are fixed
20202         // so we need to make 2 passes
20203
20204         for(i = 0; i < len; i++){
20205             c = cs[i];
20206             m = c.getPositionEl().getMargins('lr');
20207             margins[i] = m;
20208             if(!c.columnWidth){
20209                 pw -= (c.getWidth() + m);
20210             }
20211         }
20212
20213         pw = pw < 0 ? 0 : pw;
20214
20215         for(i = 0; i < len; i++){
20216             c = cs[i];
20217             m = margins[i];
20218             if(c.columnWidth){
20219                 c.setSize(Math.floor(c.columnWidth * pw) - m);
20220             }
20221         }
20222
20223         // Browsers differ as to when they account for scrollbars.  We need to re-measure to see if the scrollbar
20224         // spaces were accounted for properly.  If not, re-layout.
20225         if (Ext.isIE) {
20226             if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
20227                 var ts = this.getLayoutTargetSize();
20228                 if (ts.width != size.width){
20229                     this.adjustmentPass = true;
20230                     this.onLayout(ct, target);
20231                 }
20232             }
20233         }
20234         delete this.adjustmentPass;
20235     }
20236
20237     /**
20238      * @property activeItem
20239      * @hide
20240      */
20241 });
20242
20243 Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;
20244 /**
20245  * @class Ext.layout.BorderLayout
20246  * @extends Ext.layout.ContainerLayout
20247  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
20248  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
20249  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
20250  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
20251  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
20252  * {@link Ext.Container#layout} config, and should generally not need to be created directly
20253  * via the new keyword.</p>
20254  * <p>BorderLayout does not have any direct config options (other than inherited ones).
20255  * All configuration options available for customizing the BorderLayout are at the
20256  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
20257  * levels.</p>
20258  * <p>Example usage:</p>
20259  * <pre><code>
20260 var myBorderPanel = new Ext.Panel({
20261     {@link Ext.Component#renderTo renderTo}: document.body,
20262     {@link Ext.BoxComponent#width width}: 700,
20263     {@link Ext.BoxComponent#height height}: 500,
20264     {@link Ext.Panel#title title}: 'Border Layout',
20265     {@link Ext.Container#layout layout}: 'border',
20266     {@link Ext.Container#items items}: [{
20267         {@link Ext.Panel#title title}: 'South Region is resizable',
20268         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
20269         {@link Ext.BoxComponent#height height}: 100,
20270         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
20271         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
20272         {@link Ext.SplitBar#maxSize maxSize}: 150,
20273         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
20274     },{
20275         // xtype: 'panel' implied by default
20276         {@link Ext.Panel#title title}: 'West Region is collapsible',
20277         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
20278         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
20279         {@link Ext.BoxComponent#width width}: 200,
20280         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
20281         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
20282         {@link Ext.Component#id id}: 'west-region-container',
20283         {@link Ext.Container#layout layout}: 'fit',
20284         {@link Ext.Panel#unstyled unstyled}: true
20285     },{
20286         {@link Ext.Panel#title title}: 'Center Region',
20287         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
20288         {@link Ext.Component#xtype xtype}: 'container',
20289         {@link Ext.Container#layout layout}: 'fit',
20290         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
20291     }]
20292 });
20293 </code></pre>
20294  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
20295  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
20296  * The child item in the center region will always be resized to fill the remaining space not used by
20297  * the other regions in the layout.</li>
20298  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
20299  * (an integer representing the number of pixels that the region should take up).</li>
20300  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
20301  * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.  To add/remove
20302  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
20303  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
20304  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
20305  * is added to the west region:
20306  * <div style="margin-left:16px"><pre><code>
20307 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
20308 wrc.{@link Ext.Panel#removeAll removeAll}();
20309 wrc.{@link Ext.Container#add add}({
20310     title: 'Added Panel',
20311     html: 'Some content'
20312 });
20313 wrc.{@link Ext.Container#doLayout doLayout}();
20314  * </code></pre></div>
20315  * </li>
20316  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
20317  * <div style="margin-left:16px"><pre><code>
20318 wr = myBorderPanel.layout.west;
20319  * </code></pre></div>
20320  * </li>
20321  * </ul></div>
20322  */
20323 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
20324     // private
20325     monitorResize:true,
20326     // private
20327     rendered : false,
20328
20329     type: 'border',
20330
20331     targetCls: 'x-border-layout-ct',
20332
20333     getLayoutTargetSize : function() {
20334         var target = this.container.getLayoutTarget();
20335         return target ? target.getViewSize() : {};
20336     },
20337
20338     // private
20339     onLayout : function(ct, target){
20340         var collapsed, i, c, pos, items = ct.items.items, len = items.length;
20341         if(!this.rendered){
20342             collapsed = [];
20343             for(i = 0; i < len; i++) {
20344                 c = items[i];
20345                 pos = c.region;
20346                 if(c.collapsed){
20347                     collapsed.push(c);
20348                 }
20349                 c.collapsed = false;
20350                 if(!c.rendered){
20351                     c.render(target, i);
20352                     c.getPositionEl().addClass('x-border-panel');
20353                 }
20354                 this[pos] = pos != 'center' && c.split ?
20355                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
20356                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
20357                 this[pos].render(target, c);
20358             }
20359             this.rendered = true;
20360         }
20361
20362         var size = this.getLayoutTargetSize();
20363         if(size.width < 20 || size.height < 20){ // display none?
20364             if(collapsed){
20365                 this.restoreCollapsed = collapsed;
20366             }
20367             return;
20368         }else if(this.restoreCollapsed){
20369             collapsed = this.restoreCollapsed;
20370             delete this.restoreCollapsed;
20371         }
20372
20373         var w = size.width, h = size.height,
20374             centerW = w, centerH = h, centerY = 0, centerX = 0,
20375             n = this.north, s = this.south, west = this.west, e = this.east, c = this.center,
20376             b, m, totalWidth, totalHeight;
20377         if(!c && Ext.layout.BorderLayout.WARN !== false){
20378             throw 'No center region defined in BorderLayout ' + ct.id;
20379         }
20380
20381         if(n && n.isVisible()){
20382             b = n.getSize();
20383             m = n.getMargins();
20384             b.width = w - (m.left+m.right);
20385             b.x = m.left;
20386             b.y = m.top;
20387             centerY = b.height + b.y + m.bottom;
20388             centerH -= centerY;
20389             n.applyLayout(b);
20390         }
20391         if(s && s.isVisible()){
20392             b = s.getSize();
20393             m = s.getMargins();
20394             b.width = w - (m.left+m.right);
20395             b.x = m.left;
20396             totalHeight = (b.height + m.top + m.bottom);
20397             b.y = h - totalHeight + m.top;
20398             centerH -= totalHeight;
20399             s.applyLayout(b);
20400         }
20401         if(west && west.isVisible()){
20402             b = west.getSize();
20403             m = west.getMargins();
20404             b.height = centerH - (m.top+m.bottom);
20405             b.x = m.left;
20406             b.y = centerY + m.top;
20407             totalWidth = (b.width + m.left + m.right);
20408             centerX += totalWidth;
20409             centerW -= totalWidth;
20410             west.applyLayout(b);
20411         }
20412         if(e && e.isVisible()){
20413             b = e.getSize();
20414             m = e.getMargins();
20415             b.height = centerH - (m.top+m.bottom);
20416             totalWidth = (b.width + m.left + m.right);
20417             b.x = w - totalWidth + m.left;
20418             b.y = centerY + m.top;
20419             centerW -= totalWidth;
20420             e.applyLayout(b);
20421         }
20422         if(c){
20423             m = c.getMargins();
20424             var centerBox = {
20425                 x: centerX + m.left,
20426                 y: centerY + m.top,
20427                 width: centerW - (m.left+m.right),
20428                 height: centerH - (m.top+m.bottom)
20429             };
20430             c.applyLayout(centerBox);
20431         }
20432         if(collapsed){
20433             for(i = 0, len = collapsed.length; i < len; i++){
20434                 collapsed[i].collapse(false);
20435             }
20436         }
20437         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
20438             target.repaint();
20439         }
20440         // Putting a border layout into an overflowed container is NOT correct and will make a second layout pass necessary.
20441         if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
20442             var ts = this.getLayoutTargetSize();
20443             if (ts.width != size.width || ts.height != size.height){
20444                 this.adjustmentPass = true;
20445                 this.onLayout(ct, target);
20446             }
20447         }
20448         delete this.adjustmentPass;
20449     },
20450
20451     destroy: function() {
20452         var r = ['north', 'south', 'east', 'west'], i, region;
20453         for (i = 0; i < r.length; i++) {
20454             region = this[r[i]];
20455             if(region){
20456                 if(region.destroy){
20457                     region.destroy();
20458                 }else if (region.split){
20459                     region.split.destroy(true);
20460                 }
20461             }
20462         }
20463         Ext.layout.BorderLayout.superclass.destroy.call(this);
20464     }
20465
20466     /**
20467      * @property activeItem
20468      * @hide
20469      */
20470 });
20471
20472 /**
20473  * @class Ext.layout.BorderLayout.Region
20474  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
20475  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
20476  * independent of other regions and the containing BorderLayout, and can be any of the
20477  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
20478  * <p>Region size is managed automatically and cannot be changed by the user -- for
20479  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
20480  * @constructor
20481  * Create a new Region.
20482  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
20483  * @param {Object} config The configuration options
20484  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
20485  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
20486  * <b>must have a center region</b> for the primary content -- all other regions are optional.
20487  */
20488 Ext.layout.BorderLayout.Region = function(layout, config, pos){
20489     Ext.apply(this, config);
20490     this.layout = layout;
20491     this.position = pos;
20492     this.state = {};
20493     if(typeof this.margins == 'string'){
20494         this.margins = this.layout.parseMargins(this.margins);
20495     }
20496     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
20497     if(this.collapsible){
20498         if(typeof this.cmargins == 'string'){
20499             this.cmargins = this.layout.parseMargins(this.cmargins);
20500         }
20501         if(this.collapseMode == 'mini' && !this.cmargins){
20502             this.cmargins = {left:0,top:0,right:0,bottom:0};
20503         }else{
20504             this.cmargins = Ext.applyIf(this.cmargins || {},
20505                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
20506         }
20507     }
20508 };
20509
20510 Ext.layout.BorderLayout.Region.prototype = {
20511     /**
20512      * @cfg {Boolean} animFloat
20513      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
20514      * panel that will close again once the user mouses out of that panel (or clicks out if
20515      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
20516      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
20517      */
20518     /**
20519      * @cfg {Boolean} autoHide
20520      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
20521      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
20522      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
20523      * user clicks outside of the panel (defaults to <tt>true</tt>).
20524      */
20525     /**
20526      * @cfg {String} collapseMode
20527      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
20528      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
20529      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
20530      * title bar.</div></li>
20531      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
20532      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
20533      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
20534      * </div></li>
20535      * </ul></div></p>
20536      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
20537      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
20538      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
20539      * <p>See also <tt>{@link #cmargins}</tt>.</p>
20540      */
20541     /**
20542      * @cfg {Object} margins
20543      * An object containing margins to apply to the region when in the expanded state in the
20544      * format:<pre><code>
20545 {
20546     top: (top margin),
20547     right: (right margin),
20548     bottom: (bottom margin),
20549     left: (left margin)
20550 }</code></pre>
20551      * <p>May also be a string containing space-separated, numeric margin values. The order of the
20552      * sides associated with each value matches the way CSS processes margin values:</p>
20553      * <p><div class="mdetail-params"><ul>
20554      * <li>If there is only one value, it applies to all sides.</li>
20555      * <li>If there are two values, the top and bottom borders are set to the first value and the
20556      * right and left are set to the second.</li>
20557      * <li>If there are three values, the top is set to the first value, the left and right are set
20558      * to the second, and the bottom is set to the third.</li>
20559      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
20560      * </ul></div></p>
20561      * <p>Defaults to:</p><pre><code>
20562      * {top:0, right:0, bottom:0, left:0}
20563      * </code></pre>
20564      */
20565     /**
20566      * @cfg {Object} cmargins
20567      * An object containing margins to apply to the region when in the collapsed state in the
20568      * format:<pre><code>
20569 {
20570     top: (top margin),
20571     right: (right margin),
20572     bottom: (bottom margin),
20573     left: (left margin)
20574 }</code></pre>
20575      * <p>May also be a string containing space-separated, numeric margin values. The order of the
20576      * sides associated with each value matches the way CSS processes margin values.</p>
20577      * <p><ul>
20578      * <li>If there is only one value, it applies to all sides.</li>
20579      * <li>If there are two values, the top and bottom borders are set to the first value and the
20580      * right and left are set to the second.</li>
20581      * <li>If there are three values, the top is set to the first value, the left and right are set
20582      * to the second, and the bottom is set to the third.</li>
20583      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
20584      * </ul></p>
20585      */
20586     /**
20587      * @cfg {Boolean} collapsible
20588      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
20589      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
20590      * bar of the region, otherwise the button will not be shown.</p>
20591      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
20592      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
20593      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
20594      */
20595     collapsible : false,
20596     /**
20597      * @cfg {Boolean} split
20598      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and
20599      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
20600      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
20601      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
20602      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
20603      * <li>this configuration option is ignored if <tt>region='center'</tt></li>
20604      * <li>when <tt>split == true</tt>, it is common to specify a
20605      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
20606      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
20607      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
20608      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
20609      * for the collapse tool</tt></li>
20610      * </ul></div>
20611      */
20612     split:false,
20613     /**
20614      * @cfg {Boolean} floatable
20615      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
20616      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
20617      * clicking the expand button to see it again (defaults to <tt>true</tt>).
20618      */
20619     floatable: true,
20620     /**
20621      * @cfg {Number} minWidth
20622      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
20623      * <tt>maxWidth</tt> may also be specified.</p><br>
20624      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
20625      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
20626      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
20627      */
20628     minWidth:50,
20629     /**
20630      * @cfg {Number} minHeight
20631      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
20632      * <tt>maxHeight</tt> may also be specified.</p><br>
20633      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
20634      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
20635      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
20636      */
20637     minHeight:50,
20638
20639     // private
20640     defaultMargins : {left:0,top:0,right:0,bottom:0},
20641     // private
20642     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
20643     // private
20644     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
20645     floatingZIndex: 100,
20646
20647     /**
20648      * True if this region is collapsed. Read-only.
20649      * @type Boolean
20650      * @property
20651      */
20652     isCollapsed : false,
20653
20654     /**
20655      * This region's panel.  Read-only.
20656      * @type Ext.Panel
20657      * @property panel
20658      */
20659     /**
20660      * This region's layout.  Read-only.
20661      * @type Layout
20662      * @property layout
20663      */
20664     /**
20665      * This region's layout position (north, south, east, west or center).  Read-only.
20666      * @type String
20667      * @property position
20668      */
20669
20670     // private
20671     render : function(ct, p){
20672         this.panel = p;
20673         p.el.enableDisplayMode();
20674         this.targetEl = ct;
20675         this.el = p.el;
20676
20677         var gs = p.getState, ps = this.position;
20678         p.getState = function(){
20679             return Ext.apply(gs.call(p) || {}, this.state);
20680         }.createDelegate(this);
20681
20682         if(ps != 'center'){
20683             p.allowQueuedExpand = false;
20684             p.on({
20685                 beforecollapse: this.beforeCollapse,
20686                 collapse: this.onCollapse,
20687                 beforeexpand: this.beforeExpand,
20688                 expand: this.onExpand,
20689                 hide: this.onHide,
20690                 show: this.onShow,
20691                 scope: this
20692             });
20693             if(this.collapsible || this.floatable){
20694                 p.collapseEl = 'el';
20695                 p.slideAnchor = this.getSlideAnchor();
20696             }
20697             if(p.tools && p.tools.toggle){
20698                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
20699                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
20700             }
20701         }
20702     },
20703
20704     // private
20705     getCollapsedEl : function(){
20706         if(!this.collapsedEl){
20707             if(!this.toolTemplate){
20708                 var tt = new Ext.Template(
20709                      '<div class="x-tool x-tool-{id}">&#160;</div>'
20710                 );
20711                 tt.disableFormats = true;
20712                 tt.compile();
20713                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
20714             }
20715             this.collapsedEl = this.targetEl.createChild({
20716                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
20717                 id: this.panel.id + '-xcollapsed'
20718             });
20719             this.collapsedEl.enableDisplayMode('block');
20720
20721             if(this.collapseMode == 'mini'){
20722                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
20723                 this.miniCollapsedEl = this.collapsedEl.createChild({
20724                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
20725                 });
20726                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
20727                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
20728                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
20729             }else {
20730                 if(this.collapsible !== false && !this.hideCollapseTool) {
20731                     var t = this.toolTemplate.append(
20732                             this.collapsedEl.dom,
20733                             {id:'expand-'+this.position}, true);
20734                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
20735                     t.on('click', this.onExpandClick, this, {stopEvent:true});
20736                 }
20737                 if(this.floatable !== false || this.titleCollapse){
20738                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
20739                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
20740                 }
20741             }
20742         }
20743         return this.collapsedEl;
20744     },
20745
20746     // private
20747     onExpandClick : function(e){
20748         if(this.isSlid){
20749             this.panel.expand(false);
20750         }else{
20751             this.panel.expand();
20752         }
20753     },
20754
20755     // private
20756     onCollapseClick : function(e){
20757         this.panel.collapse();
20758     },
20759
20760     // private
20761     beforeCollapse : function(p, animate){
20762         this.lastAnim = animate;
20763         if(this.splitEl){
20764             this.splitEl.hide();
20765         }
20766         this.getCollapsedEl().show();
20767         var el = this.panel.getEl();
20768         this.originalZIndex = el.getStyle('z-index');
20769         el.setStyle('z-index', 100);
20770         this.isCollapsed = true;
20771         this.layout.layout();
20772     },
20773
20774     // private
20775     onCollapse : function(animate){
20776         this.panel.el.setStyle('z-index', 1);
20777         if(this.lastAnim === false || this.panel.animCollapse === false){
20778             this.getCollapsedEl().dom.style.visibility = 'visible';
20779         }else{
20780             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
20781         }
20782         this.state.collapsed = true;
20783         this.panel.saveState();
20784     },
20785
20786     // private
20787     beforeExpand : function(animate){
20788         if(this.isSlid){
20789             this.afterSlideIn();
20790         }
20791         var c = this.getCollapsedEl();
20792         this.el.show();
20793         if(this.position == 'east' || this.position == 'west'){
20794             this.panel.setSize(undefined, c.getHeight());
20795         }else{
20796             this.panel.setSize(c.getWidth(), undefined);
20797         }
20798         c.hide();
20799         c.dom.style.visibility = 'hidden';
20800         this.panel.el.setStyle('z-index', this.floatingZIndex);
20801     },
20802
20803     // private
20804     onExpand : function(){
20805         this.isCollapsed = false;
20806         if(this.splitEl){
20807             this.splitEl.show();
20808         }
20809         this.layout.layout();
20810         this.panel.el.setStyle('z-index', this.originalZIndex);
20811         this.state.collapsed = false;
20812         this.panel.saveState();
20813     },
20814
20815     // private
20816     collapseClick : function(e){
20817         if(this.isSlid){
20818            e.stopPropagation();
20819            this.slideIn();
20820         }else{
20821            e.stopPropagation();
20822            this.slideOut();
20823         }
20824     },
20825
20826     // private
20827     onHide : function(){
20828         if(this.isCollapsed){
20829             this.getCollapsedEl().hide();
20830         }else if(this.splitEl){
20831             this.splitEl.hide();
20832         }
20833     },
20834
20835     // private
20836     onShow : function(){
20837         if(this.isCollapsed){
20838             this.getCollapsedEl().show();
20839         }else if(this.splitEl){
20840             this.splitEl.show();
20841         }
20842     },
20843
20844     /**
20845      * True if this region is currently visible, else false.
20846      * @return {Boolean}
20847      */
20848     isVisible : function(){
20849         return !this.panel.hidden;
20850     },
20851
20852     /**
20853      * Returns the current margins for this region.  If the region is collapsed, the
20854      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
20855      * {@link #margins} value will be returned.
20856      * @return {Object} An object containing the element's margins: <tt>{left: (left
20857      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
20858      */
20859     getMargins : function(){
20860         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
20861     },
20862
20863     /**
20864      * Returns the current size of this region.  If the region is collapsed, the size of the
20865      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
20866      * @return {Object} An object containing the element's size: <tt>{width: (element width),
20867      * height: (element height)}</tt>
20868      */
20869     getSize : function(){
20870         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
20871     },
20872
20873     /**
20874      * Sets the specified panel as the container element for this region.
20875      * @param {Ext.Panel} panel The new panel
20876      */
20877     setPanel : function(panel){
20878         this.panel = panel;
20879     },
20880
20881     /**
20882      * Returns the minimum allowable width for this region.
20883      * @return {Number} The minimum width
20884      */
20885     getMinWidth: function(){
20886         return this.minWidth;
20887     },
20888
20889     /**
20890      * Returns the minimum allowable height for this region.
20891      * @return {Number} The minimum height
20892      */
20893     getMinHeight: function(){
20894         return this.minHeight;
20895     },
20896
20897     // private
20898     applyLayoutCollapsed : function(box){
20899         var ce = this.getCollapsedEl();
20900         ce.setLeftTop(box.x, box.y);
20901         ce.setSize(box.width, box.height);
20902     },
20903
20904     // private
20905     applyLayout : function(box){
20906         if(this.isCollapsed){
20907             this.applyLayoutCollapsed(box);
20908         }else{
20909             this.panel.setPosition(box.x, box.y);
20910             this.panel.setSize(box.width, box.height);
20911         }
20912     },
20913
20914     // private
20915     beforeSlide: function(){
20916         this.panel.beforeEffect();
20917     },
20918
20919     // private
20920     afterSlide : function(){
20921         this.panel.afterEffect();
20922     },
20923
20924     // private
20925     initAutoHide : function(){
20926         if(this.autoHide !== false){
20927             if(!this.autoHideHd){
20928                 this.autoHideSlideTask = new Ext.util.DelayedTask(this.slideIn, this);
20929                 this.autoHideHd = {
20930                     "mouseout": function(e){
20931                         if(!e.within(this.el, true)){
20932                             this.autoHideSlideTask.delay(500);
20933                         }
20934                     },
20935                     "mouseover" : function(e){
20936                         this.autoHideSlideTask.cancel();
20937                     },
20938                     scope : this
20939                 };
20940             }
20941             this.el.on(this.autoHideHd);
20942             this.collapsedEl.on(this.autoHideHd);
20943         }
20944     },
20945
20946     // private
20947     clearAutoHide : function(){
20948         if(this.autoHide !== false){
20949             this.el.un("mouseout", this.autoHideHd.mouseout);
20950             this.el.un("mouseover", this.autoHideHd.mouseover);
20951             this.collapsedEl.un("mouseout", this.autoHideHd.mouseout);
20952             this.collapsedEl.un("mouseover", this.autoHideHd.mouseover);
20953         }
20954     },
20955
20956     // private
20957     clearMonitor : function(){
20958         Ext.getDoc().un("click", this.slideInIf, this);
20959     },
20960
20961     /**
20962      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
20963      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
20964      * are clicked, or the mouse exits the Region.
20965      */
20966     slideOut : function(){
20967         if(this.isSlid || this.el.hasActiveFx()){
20968             return;
20969         }
20970         this.isSlid = true;
20971         var ts = this.panel.tools, dh, pc;
20972         if(ts && ts.toggle){
20973             ts.toggle.hide();
20974         }
20975         this.el.show();
20976
20977         // Temporarily clear the collapsed flag so we can onResize the panel on the slide
20978         pc = this.panel.collapsed;
20979         this.panel.collapsed = false;
20980
20981         if(this.position == 'east' || this.position == 'west'){
20982             // Temporarily clear the deferHeight flag so we can size the height on the slide
20983             dh = this.panel.deferHeight;
20984             this.panel.deferHeight = false;
20985
20986             this.panel.setSize(undefined, this.collapsedEl.getHeight());
20987
20988             // Put the deferHeight flag back after setSize
20989             this.panel.deferHeight = dh;
20990         }else{
20991             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
20992         }
20993
20994         // Put the collapsed flag back after onResize
20995         this.panel.collapsed = pc;
20996
20997         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
20998         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
20999         this.el.setStyle("z-index", this.floatingZIndex+2);
21000         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
21001         if(this.animFloat !== false){
21002             this.beforeSlide();
21003             this.el.slideIn(this.getSlideAnchor(), {
21004                 callback: function(){
21005                     this.afterSlide();
21006                     this.initAutoHide();
21007                     Ext.getDoc().on("click", this.slideInIf, this);
21008                 },
21009                 scope: this,
21010                 block: true
21011             });
21012         }else{
21013             this.initAutoHide();
21014              Ext.getDoc().on("click", this.slideInIf, this);
21015         }
21016     },
21017
21018     // private
21019     afterSlideIn : function(){
21020         this.clearAutoHide();
21021         this.isSlid = false;
21022         this.clearMonitor();
21023         this.el.setStyle("z-index", "");
21024         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
21025         this.el.dom.style.left = this.restoreLT[0];
21026         this.el.dom.style.top = this.restoreLT[1];
21027
21028         var ts = this.panel.tools;
21029         if(ts && ts.toggle){
21030             ts.toggle.show();
21031         }
21032     },
21033
21034     /**
21035      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
21036      * this region back into its collapsed state.
21037      */
21038     slideIn : function(cb){
21039         if(!this.isSlid || this.el.hasActiveFx()){
21040             Ext.callback(cb);
21041             return;
21042         }
21043         this.isSlid = false;
21044         if(this.animFloat !== false){
21045             this.beforeSlide();
21046             this.el.slideOut(this.getSlideAnchor(), {
21047                 callback: function(){
21048                     this.el.hide();
21049                     this.afterSlide();
21050                     this.afterSlideIn();
21051                     Ext.callback(cb);
21052                 },
21053                 scope: this,
21054                 block: true
21055             });
21056         }else{
21057             this.el.hide();
21058             this.afterSlideIn();
21059         }
21060     },
21061
21062     // private
21063     slideInIf : function(e){
21064         if(!e.within(this.el)){
21065             this.slideIn();
21066         }
21067     },
21068
21069     // private
21070     anchors : {
21071         "west" : "left",
21072         "east" : "right",
21073         "north" : "top",
21074         "south" : "bottom"
21075     },
21076
21077     // private
21078     sanchors : {
21079         "west" : "l",
21080         "east" : "r",
21081         "north" : "t",
21082         "south" : "b"
21083     },
21084
21085     // private
21086     canchors : {
21087         "west" : "tl-tr",
21088         "east" : "tr-tl",
21089         "north" : "tl-bl",
21090         "south" : "bl-tl"
21091     },
21092
21093     // private
21094     getAnchor : function(){
21095         return this.anchors[this.position];
21096     },
21097
21098     // private
21099     getCollapseAnchor : function(){
21100         return this.canchors[this.position];
21101     },
21102
21103     // private
21104     getSlideAnchor : function(){
21105         return this.sanchors[this.position];
21106     },
21107
21108     // private
21109     getAlignAdj : function(){
21110         var cm = this.cmargins;
21111         switch(this.position){
21112             case "west":
21113                 return [0, 0];
21114             break;
21115             case "east":
21116                 return [0, 0];
21117             break;
21118             case "north":
21119                 return [0, 0];
21120             break;
21121             case "south":
21122                 return [0, 0];
21123             break;
21124         }
21125     },
21126
21127     // private
21128     getExpandAdj : function(){
21129         var c = this.collapsedEl, cm = this.cmargins;
21130         switch(this.position){
21131             case "west":
21132                 return [-(cm.right+c.getWidth()+cm.left), 0];
21133             break;
21134             case "east":
21135                 return [cm.right+c.getWidth()+cm.left, 0];
21136             break;
21137             case "north":
21138                 return [0, -(cm.top+cm.bottom+c.getHeight())];
21139             break;
21140             case "south":
21141                 return [0, cm.top+cm.bottom+c.getHeight()];
21142             break;
21143         }
21144     },
21145
21146     destroy : function(){
21147         if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
21148             this.autoHideSlideTask.cancel();
21149         }
21150         Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
21151     }
21152 };
21153
21154 /**
21155  * @class Ext.layout.BorderLayout.SplitRegion
21156  * @extends Ext.layout.BorderLayout.Region
21157  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
21158  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
21159  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
21160  * @constructor
21161  * Create a new SplitRegion.
21162  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
21163  * @param {Object} config The configuration options
21164  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
21165  * BorderLayout must have a center region for the primary content -- all other regions are optional.
21166  */
21167 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
21168     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
21169     // prevent switch
21170     this.applyLayout = this.applyFns[pos];
21171 };
21172
21173 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
21174     /**
21175      * @cfg {Number} tickSize
21176      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
21177      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
21178      */
21179     /**
21180      * @cfg {String} splitTip
21181      * The tooltip to display when the user hovers over a
21182      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
21183      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
21184      * <tt>{@link #useSplitTips} = true</tt>.
21185      */
21186     splitTip : "Drag to resize.",
21187     /**
21188      * @cfg {String} collapsibleSplitTip
21189      * The tooltip to display when the user hovers over a
21190      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
21191      * (defaults to "Drag to resize. Double click to hide."). Only applies if
21192      * <tt>{@link #useSplitTips} = true</tt>.
21193      */
21194     collapsibleSplitTip : "Drag to resize. Double click to hide.",
21195     /**
21196      * @cfg {Boolean} useSplitTips
21197      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
21198      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
21199      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
21200      */
21201     useSplitTips : false,
21202
21203     // private
21204     splitSettings : {
21205         north : {
21206             orientation: Ext.SplitBar.VERTICAL,
21207             placement: Ext.SplitBar.TOP,
21208             maxFn : 'getVMaxSize',
21209             minProp: 'minHeight',
21210             maxProp: 'maxHeight'
21211         },
21212         south : {
21213             orientation: Ext.SplitBar.VERTICAL,
21214             placement: Ext.SplitBar.BOTTOM,
21215             maxFn : 'getVMaxSize',
21216             minProp: 'minHeight',
21217             maxProp: 'maxHeight'
21218         },
21219         east : {
21220             orientation: Ext.SplitBar.HORIZONTAL,
21221             placement: Ext.SplitBar.RIGHT,
21222             maxFn : 'getHMaxSize',
21223             minProp: 'minWidth',
21224             maxProp: 'maxWidth'
21225         },
21226         west : {
21227             orientation: Ext.SplitBar.HORIZONTAL,
21228             placement: Ext.SplitBar.LEFT,
21229             maxFn : 'getHMaxSize',
21230             minProp: 'minWidth',
21231             maxProp: 'maxWidth'
21232         }
21233     },
21234
21235     // private
21236     applyFns : {
21237         west : function(box){
21238             if(this.isCollapsed){
21239                 return this.applyLayoutCollapsed(box);
21240             }
21241             var sd = this.splitEl.dom, s = sd.style;
21242             this.panel.setPosition(box.x, box.y);
21243             var sw = sd.offsetWidth;
21244             s.left = (box.x+box.width-sw)+'px';
21245             s.top = (box.y)+'px';
21246             s.height = Math.max(0, box.height)+'px';
21247             this.panel.setSize(box.width-sw, box.height);
21248         },
21249         east : function(box){
21250             if(this.isCollapsed){
21251                 return this.applyLayoutCollapsed(box);
21252             }
21253             var sd = this.splitEl.dom, s = sd.style;
21254             var sw = sd.offsetWidth;
21255             this.panel.setPosition(box.x+sw, box.y);
21256             s.left = (box.x)+'px';
21257             s.top = (box.y)+'px';
21258             s.height = Math.max(0, box.height)+'px';
21259             this.panel.setSize(box.width-sw, box.height);
21260         },
21261         north : function(box){
21262             if(this.isCollapsed){
21263                 return this.applyLayoutCollapsed(box);
21264             }
21265             var sd = this.splitEl.dom, s = sd.style;
21266             var sh = sd.offsetHeight;
21267             this.panel.setPosition(box.x, box.y);
21268             s.left = (box.x)+'px';
21269             s.top = (box.y+box.height-sh)+'px';
21270             s.width = Math.max(0, box.width)+'px';
21271             this.panel.setSize(box.width, box.height-sh);
21272         },
21273         south : function(box){
21274             if(this.isCollapsed){
21275                 return this.applyLayoutCollapsed(box);
21276             }
21277             var sd = this.splitEl.dom, s = sd.style;
21278             var sh = sd.offsetHeight;
21279             this.panel.setPosition(box.x, box.y+sh);
21280             s.left = (box.x)+'px';
21281             s.top = (box.y)+'px';
21282             s.width = Math.max(0, box.width)+'px';
21283             this.panel.setSize(box.width, box.height-sh);
21284         }
21285     },
21286
21287     // private
21288     render : function(ct, p){
21289         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
21290
21291         var ps = this.position;
21292
21293         this.splitEl = ct.createChild({
21294             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
21295             id: this.panel.id + '-xsplit'
21296         });
21297
21298         if(this.collapseMode == 'mini'){
21299             this.miniSplitEl = this.splitEl.createChild({
21300                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
21301             });
21302             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
21303             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
21304         }
21305
21306         var s = this.splitSettings[ps];
21307
21308         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
21309         this.split.tickSize = this.tickSize;
21310         this.split.placement = s.placement;
21311         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
21312         this.split.minSize = this.minSize || this[s.minProp];
21313         this.split.on("beforeapply", this.onSplitMove, this);
21314         this.split.useShim = this.useShim === true;
21315         this.maxSize = this.maxSize || this[s.maxProp];
21316
21317         if(p.hidden){
21318             this.splitEl.hide();
21319         }
21320
21321         if(this.useSplitTips){
21322             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
21323         }
21324         if(this.collapsible){
21325             this.splitEl.on("dblclick", this.onCollapseClick,  this);
21326         }
21327     },
21328
21329     //docs inherit from superclass
21330     getSize : function(){
21331         if(this.isCollapsed){
21332             return this.collapsedEl.getSize();
21333         }
21334         var s = this.panel.getSize();
21335         if(this.position == 'north' || this.position == 'south'){
21336             s.height += this.splitEl.dom.offsetHeight;
21337         }else{
21338             s.width += this.splitEl.dom.offsetWidth;
21339         }
21340         return s;
21341     },
21342
21343     // private
21344     getHMaxSize : function(){
21345          var cmax = this.maxSize || 10000;
21346          var center = this.layout.center;
21347          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
21348     },
21349
21350     // private
21351     getVMaxSize : function(){
21352         var cmax = this.maxSize || 10000;
21353         var center = this.layout.center;
21354         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
21355     },
21356
21357     // private
21358     onSplitMove : function(split, newSize){
21359         var s = this.panel.getSize();
21360         this.lastSplitSize = newSize;
21361         if(this.position == 'north' || this.position == 'south'){
21362             this.panel.setSize(s.width, newSize);
21363             this.state.height = newSize;
21364         }else{
21365             this.panel.setSize(newSize, s.height);
21366             this.state.width = newSize;
21367         }
21368         this.layout.layout();
21369         this.panel.saveState();
21370         return false;
21371     },
21372
21373     /**
21374      * Returns a reference to the split bar in use by this region.
21375      * @return {Ext.SplitBar} The split bar
21376      */
21377     getSplitBar : function(){
21378         return this.split;
21379     },
21380
21381     // inherit docs
21382     destroy : function() {
21383         Ext.destroy(this.miniSplitEl, this.split, this.splitEl);
21384         Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this);
21385     }
21386 });
21387
21388 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/**
21389  * @class Ext.layout.FormLayout
21390  * @extends Ext.layout.AnchorLayout
21391  * <p>This layout manager is specifically designed for rendering and managing child Components of
21392  * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
21393  * {@link Ext.form.Field Field}s.</p>
21394  *
21395  * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
21396  * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
21397  * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
21398  *
21399  * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
21400  * (which is configured with FormLayout as its layout class by default) since it also provides built-in
21401  * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
21402  *
21403  * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
21404  * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
21405  * layout-specific config properties:<div class="mdetail-params"><ul>
21406  * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
21407  * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
21408  * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
21409  * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
21410  * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
21411  * </ul></div></p>
21412  *
21413  * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
21414  * <div class="mdetail-params"><ul>
21415  * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
21416  * </ul></div></p>
21417  *
21418  * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
21419  * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
21420  * in this way may be configured with the following options which affect the way the FormLayout renders them:
21421  * <div class="mdetail-params"><ul>
21422  * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
21423  * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
21424  * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
21425  * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
21426  * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
21427  * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
21428  * </ul></div></p>
21429  *
21430  * <p>Example usage:</p>
21431  * <pre><code>
21432 // Required if showing validation messages
21433 Ext.QuickTips.init();
21434
21435 // While you can create a basic Panel with layout:'form', practically
21436 // you should usually use a FormPanel to also get its form functionality
21437 // since it already creates a FormLayout internally.
21438 var form = new Ext.form.FormPanel({
21439     title: 'Form Layout',
21440     bodyStyle: 'padding:15px',
21441     width: 350,
21442     defaultType: 'textfield',
21443     defaults: {
21444         // applied to each contained item
21445         width: 230,
21446         msgTarget: 'side'
21447     },
21448     items: [{
21449             fieldLabel: 'First Name',
21450             name: 'first',
21451             allowBlank: false,
21452             {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
21453         },{
21454             fieldLabel: 'Last Name',
21455             name: 'last'
21456         },{
21457             fieldLabel: 'Email',
21458             name: 'email',
21459             vtype:'email'
21460         }, {
21461             xtype: 'textarea',
21462             hideLabel: true,     // override hideLabels layout config
21463             name: 'msg',
21464             anchor: '100% -53'
21465         }
21466     ],
21467     buttons: [
21468         {text: 'Save'},
21469         {text: 'Cancel'}
21470     ],
21471     layoutConfig: {
21472         {@link #labelSeparator}: '~' // superseded by assignment below
21473     },
21474     // config options applicable to container when layout='form':
21475     hideLabels: false,
21476     labelAlign: 'left',   // or 'right' or 'top'
21477     {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
21478     labelWidth: 65,       // defaults to 100
21479     labelPad: 8           // defaults to 5, must specify labelWidth to be honored
21480 });
21481 </code></pre>
21482  */
21483 Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
21484
21485     /**
21486      * @cfg {String} labelSeparator
21487      * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}.  Configuration
21488      * of this property at the <b>container</b> level takes precedence.
21489      */
21490     labelSeparator : ':',
21491
21492     /**
21493      * Read only. The CSS style specification string added to field labels in this layout if not
21494      * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
21495      * @type String
21496      * @property labelStyle
21497      */
21498
21499     /**
21500      * @cfg {Boolean} trackLabels
21501      * True to show/hide the field label when the field is hidden. Defaults to <tt>false</tt>.
21502      */
21503     trackLabels: false,
21504
21505     type: 'form',
21506
21507     onRemove: function(c){
21508         Ext.layout.FormLayout.superclass.onRemove.call(this, c);
21509         if(this.trackLabels){
21510             c.un('show', this.onFieldShow, this);
21511             c.un('hide', this.onFieldHide, this);
21512         }
21513         // check for itemCt, since we may be removing a fieldset or something similar
21514         var el = c.getPositionEl(),
21515             ct = c.getItemCt && c.getItemCt();
21516         if (c.rendered && ct) {
21517             if (el && el.dom) {
21518                 el.insertAfter(ct);
21519             }
21520             Ext.destroy(ct);
21521             Ext.destroyMembers(c, 'label', 'itemCt');
21522             if (c.customItemCt) {
21523                 Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
21524             }
21525         }
21526     },
21527
21528     // private
21529     setContainer : function(ct){
21530         Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
21531         if(ct.labelAlign){
21532             ct.addClass('x-form-label-'+ct.labelAlign);
21533         }
21534
21535         if(ct.hideLabels){
21536             Ext.apply(this, {
21537                 labelStyle: 'display:none',
21538                 elementStyle: 'padding-left:0;',
21539                 labelAdjust: 0
21540             });
21541         }else{
21542             this.labelSeparator = ct.labelSeparator || this.labelSeparator;
21543             ct.labelWidth = ct.labelWidth || 100;
21544             if(Ext.isNumber(ct.labelWidth)){
21545                 var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
21546                 Ext.apply(this, {
21547                     labelAdjust: ct.labelWidth + pad,
21548                     labelStyle: 'width:' + ct.labelWidth + 'px;',
21549                     elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px'
21550                 });
21551             }
21552             if(ct.labelAlign == 'top'){
21553                 Ext.apply(this, {
21554                     labelStyle: 'width:auto;',
21555                     labelAdjust: 0,
21556                     elementStyle: 'padding-left:0;'
21557                 });
21558             }
21559         }
21560     },
21561
21562     // private
21563     isHide: function(c){
21564         return c.hideLabel || this.container.hideLabels;
21565     },
21566
21567     onFieldShow: function(c){
21568         c.getItemCt().removeClass('x-hide-' + c.hideMode);
21569     },
21570
21571     onFieldHide: function(c){
21572         c.getItemCt().addClass('x-hide-' + c.hideMode);
21573     },
21574
21575     //private
21576     getLabelStyle: function(s){
21577         var ls = '', items = [this.labelStyle, s];
21578         for (var i = 0, len = items.length; i < len; ++i){
21579             if (items[i]){
21580                 ls += items[i];
21581                 if (ls.substr(-1, 1) != ';'){
21582                     ls += ';';
21583                 }
21584             }
21585         }
21586         return ls;
21587     },
21588
21589     /**
21590      * @cfg {Ext.Template} fieldTpl
21591      * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
21592      * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
21593 new Ext.Template(
21594     &#39;&lt;div class="x-form-item {itemCls}" tabIndex="-1">&#39;,
21595         &#39;&lt;&#108;abel for="{id}" style="{labelStyle}" class="x-form-item-&#108;abel">{&#108;abel}{labelSeparator}&lt;/&#108;abel>&#39;,
21596         &#39;&lt;div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">&#39;,
21597         &#39;&lt;/div>&lt;div class="{clearCls}">&lt;/div>&#39;,
21598     '&lt;/div>'
21599 );
21600 </code></pre>
21601      * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
21602      * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
21603      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
21604      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
21605      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
21606      * supplied at the container level.</div></li>
21607      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
21608      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
21609      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
21610      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
21611      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
21612      * field (defaults to <tt>''</tt>)</div></li>
21613      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
21614      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
21615      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
21616      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
21617      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
21618      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
21619      * </ul></div>
21620      * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
21621      */
21622
21623     /**
21624      * @private
21625      * 
21626      */
21627     renderItem : function(c, position, target){
21628         if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
21629             var args = this.getTemplateArgs(c);
21630             if(Ext.isNumber(position)){
21631                 position = target.dom.childNodes[position] || null;
21632             }
21633             if(position){
21634                 c.itemCt = this.fieldTpl.insertBefore(position, args, true);
21635             }else{
21636                 c.itemCt = this.fieldTpl.append(target, args, true);
21637             }
21638             if(!c.getItemCt){
21639                 // Non form fields don't have getItemCt, apply it here
21640                 // This will get cleaned up in onRemove
21641                 Ext.apply(c, {
21642                     getItemCt: function(){
21643                         return c.itemCt;
21644                     },
21645                     customItemCt: true
21646                 });
21647             }
21648             c.label = c.getItemCt().child('label.x-form-item-label');
21649             if(!c.rendered){
21650                 c.render('x-form-el-' + c.id);
21651             }else if(!this.isValidParent(c, target)){
21652                 Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl());
21653             }
21654             if(this.trackLabels){
21655                 if(c.hidden){
21656                     this.onFieldHide(c);
21657                 }
21658                 c.on({
21659                     scope: this,
21660                     show: this.onFieldShow,
21661                     hide: this.onFieldHide
21662                 });
21663             }
21664             this.configureItem(c);
21665         }else {
21666             Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
21667         }
21668     },
21669
21670     /**
21671      * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
21672      * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
21673      * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
21674      * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
21675      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
21676      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
21677      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
21678      * supplied at the container level.</div></li>
21679      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
21680      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
21681      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
21682      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
21683      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
21684      * field (defaults to the field's configured fieldLabel property)</div></li>
21685      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
21686      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
21687      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
21688      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
21689      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
21690      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
21691      * </ul></div>
21692      * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered.
21693      * @return {Object} An object hash containing the properties required to render the Field.
21694      */
21695     getTemplateArgs: function(field) {
21696         var noLabelSep = !field.fieldLabel || field.hideLabel;
21697         
21698         return {
21699             id            : field.id,
21700             label         : field.fieldLabel,
21701             itemCls       : (field.itemCls || this.container.itemCls || '') + (field.hideLabel ? ' x-hide-label' : ''),
21702             clearCls      : field.clearCls || 'x-form-clear-left',
21703             labelStyle    : this.getLabelStyle(field.labelStyle),
21704             elementStyle  : this.elementStyle || '',
21705             labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator)
21706         };
21707     },
21708
21709     // private
21710     adjustWidthAnchor: function(value, c){
21711         if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){
21712             var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict);
21713             return value - this.labelAdjust + (adjust ? -3 : 0);
21714         }
21715         return value;
21716     },
21717
21718     adjustHeightAnchor : function(value, c){
21719         if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){
21720             return value - c.label.getHeight();
21721         }
21722         return value;
21723     },
21724
21725     // private
21726     isValidParent : function(c, target){
21727         return target && this.container.getEl().contains(c.getPositionEl());
21728     }
21729
21730     /**
21731      * @property activeItem
21732      * @hide
21733      */
21734 });
21735
21736 Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;
21737 /**
21738  * @class Ext.layout.AccordionLayout
21739  * @extends Ext.layout.FitLayout
21740  * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only
21741  * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>
21742  * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>
21743  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
21744  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
21745  * <p>Example usage:</p>
21746  * <pre><code>
21747 var accordion = new Ext.Panel({
21748     title: 'Accordion Layout',
21749     layout:'accordion',
21750     defaults: {
21751         // applied to each contained panel
21752         bodyStyle: 'padding:15px'
21753     },
21754     layoutConfig: {
21755         // layout-specific configs go here
21756         titleCollapse: false,
21757         animate: true,
21758         activeOnTop: true
21759     },
21760     items: [{
21761         title: 'Panel 1',
21762         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
21763     },{
21764         title: 'Panel 2',
21765         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
21766     },{
21767         title: 'Panel 3',
21768         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
21769     }]
21770 });
21771 </code></pre>
21772  */
21773 Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {
21774     /**
21775      * @cfg {Boolean} fill
21776      * True to adjust the active item's height to fill the available space in the container, false to use the
21777      * item's current height, or auto height if not explicitly set (defaults to true).
21778      */
21779     fill : true,
21780     /**
21781      * @cfg {Boolean} autoWidth
21782      * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).
21783      * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within
21784      * layouts if they have auto width, so in such cases this config should be set to false.
21785      */
21786     autoWidth : true,
21787     /**
21788      * @cfg {Boolean} titleCollapse
21789      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
21790      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,
21791      * {@link #hideCollapseTool} should be false also.
21792      */
21793     titleCollapse : true,
21794     /**
21795      * @cfg {Boolean} hideCollapseTool
21796      * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).
21797      * When set to true, {@link #titleCollapse} should be true also.
21798      */
21799     hideCollapseTool : false,
21800     /**
21801      * @cfg {Boolean} collapseFirst
21802      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
21803      * in the contained panels' title bars, false to render it last (defaults to false).
21804      */
21805     collapseFirst : false,
21806     /**
21807      * @cfg {Boolean} animate
21808      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
21809      * close directly with no animation (defaults to false).  Note: to defer to the specific config setting of each
21810      * contained panel for this property, set this to undefined at the layout level.
21811      */
21812     animate : false,
21813     /**
21814      * @cfg {Boolean} sequence
21815      * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.
21816      */
21817     sequence : false,
21818     /**
21819      * @cfg {Boolean} activeOnTop
21820      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
21821      * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).
21822      */
21823     activeOnTop : false,
21824
21825     type: 'accordion',
21826
21827     renderItem : function(c){
21828         if(this.animate === false){
21829             c.animCollapse = false;
21830         }
21831         c.collapsible = true;
21832         if(this.autoWidth){
21833             c.autoWidth = true;
21834         }
21835         if(this.titleCollapse){
21836             c.titleCollapse = true;
21837         }
21838         if(this.hideCollapseTool){
21839             c.hideCollapseTool = true;
21840         }
21841         if(this.collapseFirst !== undefined){
21842             c.collapseFirst = this.collapseFirst;
21843         }
21844         if(!this.activeItem && !c.collapsed){
21845             this.setActiveItem(c, true);
21846         }else if(this.activeItem && this.activeItem != c){
21847             c.collapsed = true;
21848         }
21849         Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);
21850         c.header.addClass('x-accordion-hd');
21851         c.on('beforeexpand', this.beforeExpand, this);
21852     },
21853
21854     onRemove: function(c){
21855         Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);
21856         if(c.rendered){
21857             c.header.removeClass('x-accordion-hd');
21858         }
21859         c.un('beforeexpand', this.beforeExpand, this);
21860     },
21861
21862     // private
21863     beforeExpand : function(p, anim){
21864         var ai = this.activeItem;
21865         if(ai){
21866             if(this.sequence){
21867                 delete this.activeItem;
21868                 if (!ai.collapsed){
21869                     ai.collapse({callback:function(){
21870                         p.expand(anim || true);
21871                     }, scope: this});
21872                     return false;
21873                 }
21874             }else{
21875                 ai.collapse(this.animate);
21876             }
21877         }
21878         this.setActive(p);
21879         if(this.activeOnTop){
21880             p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);
21881         }
21882         // Items have been hidden an possibly rearranged, we need to get the container size again.
21883         this.layout();
21884     },
21885
21886     // private
21887     setItemSize : function(item, size){
21888         if(this.fill && item){
21889             var hh = 0, i, ct = this.getRenderedItems(this.container), len = ct.length, p;
21890             // Add up all the header heights
21891             for (i = 0; i < len; i++) {
21892                 if((p = ct[i]) != item){
21893                     hh += p.header.getHeight();
21894                 }
21895             };
21896             // Subtract the header heights from the container size
21897             size.height -= hh;
21898             // Call setSize on the container to set the correct height.  For Panels, deferedHeight
21899             // will simply store this size for when the expansion is done.
21900             item.setSize(size);
21901         }
21902     },
21903
21904     /**
21905      * Sets the active (expanded) item in the layout.
21906      * @param {String/Number} item The string component id or numeric index of the item to activate
21907      */
21908     setActiveItem : function(item){
21909         this.setActive(item, true);
21910     },
21911
21912     // private
21913     setActive : function(item, expand){
21914         var ai = this.activeItem;
21915         item = this.container.getComponent(item);
21916         if(ai != item){
21917             if(item.rendered && item.collapsed && expand){
21918                 item.expand();
21919             }else{
21920                 if(ai){
21921                    ai.fireEvent('deactivate', ai);
21922                 }
21923                 this.activeItem = item;
21924                 item.fireEvent('activate', item);
21925             }
21926         }
21927     }
21928 });
21929 Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;
21930
21931 //backwards compat
21932 Ext.layout.Accordion = Ext.layout.AccordionLayout;/**
21933  * @class Ext.layout.TableLayout
21934  * @extends Ext.layout.ContainerLayout
21935  * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be
21936  * specified, and rowspan and colspan can be used to create complex layouts within the table.
21937  * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,
21938  * and should generally not need to be created directly via the new keyword.</p>
21939  * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via
21940  * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout.  In the
21941  * case of TableLayout, the only valid layout config property is {@link #columns}.  However, the items added to a
21942  * TableLayout can supply the following table-specific config properties:</p>
21943  * <ul>
21944  * <li><b>rowspan</b> Applied to the table cell containing the item.</li>
21945  * <li><b>colspan</b> Applied to the table cell containing the item.</li>
21946  * <li><b>cellId</b> An id applied to the table cell containing the item.</li>
21947  * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>
21948  * </ul>
21949  * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard
21950  * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes
21951  * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
21952  * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
21953  * total column count in the layoutConfig and start adding panels in their natural order from left to right,
21954  * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,
21955  * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add
21956  * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>
21957  * <pre><code>
21958 // This code will generate a layout table that is 3 columns by 2 rows
21959 // with some spanning included.  The basic layout will be:
21960 // +--------+-----------------+
21961 // |   A    |   B             |
21962 // |        |--------+--------|
21963 // |        |   C    |   D    |
21964 // +--------+--------+--------+
21965 var table = new Ext.Panel({
21966     title: 'Table Layout',
21967     layout:'table',
21968     defaults: {
21969         // applied to each contained panel
21970         bodyStyle:'padding:20px'
21971     },
21972     layoutConfig: {
21973         // The total column count must be specified here
21974         columns: 3
21975     },
21976     items: [{
21977         html: '&lt;p&gt;Cell A content&lt;/p&gt;',
21978         rowspan: 2
21979     },{
21980         html: '&lt;p&gt;Cell B content&lt;/p&gt;',
21981         colspan: 2
21982     },{
21983         html: '&lt;p&gt;Cell C content&lt;/p&gt;',
21984         cellCls: 'highlight'
21985     },{
21986         html: '&lt;p&gt;Cell D content&lt;/p&gt;'
21987     }]
21988 });
21989 </code></pre>
21990  */
21991 Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {
21992     /**
21993      * @cfg {Number} columns
21994      * The total number of columns to create in the table for this layout.  If not specified, all Components added to
21995      * this layout will be rendered into a single row using one column per Component.
21996      */
21997
21998     // private
21999     monitorResize:false,
22000
22001     type: 'table',
22002
22003     targetCls: 'x-table-layout-ct',
22004
22005     /**
22006      * @cfg {Object} tableAttrs
22007      * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification
22008      * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>
22009 {
22010     xtype: 'panel',
22011     layout: 'table',
22012     layoutConfig: {
22013         tableAttrs: {
22014             style: {
22015                 width: '100%'
22016             }
22017         },
22018         columns: 3
22019     }
22020 }</code></pre>
22021      */
22022     tableAttrs:null,
22023
22024     // private
22025     setContainer : function(ct){
22026         Ext.layout.TableLayout.superclass.setContainer.call(this, ct);
22027
22028         this.currentRow = 0;
22029         this.currentColumn = 0;
22030         this.cells = [];
22031     },
22032     
22033     // private
22034     onLayout : function(ct, target){
22035         var cs = ct.items.items, len = cs.length, c, i;
22036
22037         if(!this.table){
22038             target.addClass('x-table-layout-ct');
22039
22040             this.table = target.createChild(
22041                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
22042         }
22043         this.renderAll(ct, target);
22044     },
22045
22046     // private
22047     getRow : function(index){
22048         var row = this.table.tBodies[0].childNodes[index];
22049         if(!row){
22050             row = document.createElement('tr');
22051             this.table.tBodies[0].appendChild(row);
22052         }
22053         return row;
22054     },
22055
22056     // private
22057     getNextCell : function(c){
22058         var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);
22059         var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];
22060         for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){
22061             if(!this.cells[rowIndex]){
22062                 this.cells[rowIndex] = [];
22063             }
22064             for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){
22065                 this.cells[rowIndex][colIndex] = true;
22066             }
22067         }
22068         var td = document.createElement('td');
22069         if(c.cellId){
22070             td.id = c.cellId;
22071         }
22072         var cls = 'x-table-layout-cell';
22073         if(c.cellCls){
22074             cls += ' ' + c.cellCls;
22075         }
22076         td.className = cls;
22077         if(c.colspan){
22078             td.colSpan = c.colspan;
22079         }
22080         if(c.rowspan){
22081             td.rowSpan = c.rowspan;
22082         }
22083         this.getRow(curRow).appendChild(td);
22084         return td;
22085     },
22086
22087     // private
22088     getNextNonSpan: function(colIndex, rowIndex){
22089         var cols = this.columns;
22090         while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {
22091             if(cols && colIndex >= cols){
22092                 rowIndex++;
22093                 colIndex = 0;
22094             }else{
22095                 colIndex++;
22096             }
22097         }
22098         return [colIndex, rowIndex];
22099     },
22100
22101     // private
22102     renderItem : function(c, position, target){
22103         // Ensure we have our inner table to get cells to render into.
22104         if(!this.table){
22105             this.table = target.createChild(
22106                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
22107         }
22108         if(c && !c.rendered){
22109             c.render(this.getNextCell(c));
22110             this.configureItem(c, position);
22111         }else if(c && !this.isValidParent(c, target)){
22112             var container = this.getNextCell(c);
22113             container.insertBefore(c.getPositionEl().dom, null);
22114             c.container = Ext.get(container);
22115             this.configureItem(c, position);
22116         }
22117     },
22118
22119     // private
22120     isValidParent : function(c, target){
22121         return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);
22122     }
22123
22124     /**
22125      * @property activeItem
22126      * @hide
22127      */
22128 });
22129
22130 Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**
22131  * @class Ext.layout.AbsoluteLayout
22132  * @extends Ext.layout.AnchorLayout
22133  * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the
22134  * ability for x/y positioning using the standard x and y component config options.</p>
22135  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
22136  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
22137  * <p>Example usage:</p>
22138  * <pre><code>
22139 var form = new Ext.form.FormPanel({
22140     title: 'Absolute Layout',
22141     layout:'absolute',
22142     layoutConfig: {
22143         // layout-specific configs go here
22144         extraCls: 'x-abs-layout-item',
22145     },
22146     baseCls: 'x-plain',
22147     url:'save-form.php',
22148     defaultType: 'textfield',
22149     items: [{
22150         x: 0,
22151         y: 5,
22152         xtype:'label',
22153         text: 'Send To:'
22154     },{
22155         x: 60,
22156         y: 0,
22157         name: 'to',
22158         anchor:'100%'  // anchor width by percentage
22159     },{
22160         x: 0,
22161         y: 35,
22162         xtype:'label',
22163         text: 'Subject:'
22164     },{
22165         x: 60,
22166         y: 30,
22167         name: 'subject',
22168         anchor: '100%'  // anchor width by percentage
22169     },{
22170         x:0,
22171         y: 60,
22172         xtype: 'textarea',
22173         name: 'msg',
22174         anchor: '100% 100%'  // anchor width and height
22175     }]
22176 });
22177 </code></pre>
22178  */
22179 Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
22180
22181     extraCls: 'x-abs-layout-item',
22182
22183     type: 'absolute',
22184
22185     onLayout : function(ct, target){
22186         target.position();
22187         this.paddingLeft = target.getPadding('l');
22188         this.paddingTop = target.getPadding('t');
22189         Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
22190     },
22191
22192     // private
22193     adjustWidthAnchor : function(value, comp){
22194         return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
22195     },
22196
22197     // private
22198     adjustHeightAnchor : function(value, comp){
22199         return  value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
22200     }
22201     /**
22202      * @property activeItem
22203      * @hide
22204      */
22205 });
22206 Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
22207 /**
22208  * @class Ext.layout.BoxLayout
22209  * @extends Ext.layout.ContainerLayout
22210  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
22211  */
22212 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
22213     /**
22214      * @cfg {Object} defaultMargins
22215      * <p>If the individual contained items do not have a <tt>margins</tt>
22216      * property specified, the default margins from this property will be
22217      * applied to each item.</p>
22218      * <br><p>This property may be specified as an object containing margins
22219      * to apply in the format:</p><pre><code>
22220 {
22221     top: (top margin),
22222     right: (right margin),
22223     bottom: (bottom margin),
22224     left: (left margin)
22225 }</code></pre>
22226      * <p>This property may also be specified as a string containing
22227      * space-separated, numeric margin values. The order of the sides associated
22228      * with each value matches the way CSS processes margin values:</p>
22229      * <div class="mdetail-params"><ul>
22230      * <li>If there is only one value, it applies to all sides.</li>
22231      * <li>If there are two values, the top and bottom borders are set to the
22232      * first value and the right and left are set to the second.</li>
22233      * <li>If there are three values, the top is set to the first value, the left
22234      * and right are set to the second, and the bottom is set to the third.</li>
22235      * <li>If there are four values, they apply to the top, right, bottom, and
22236      * left, respectively.</li>
22237      * </ul></div>
22238      * <p>Defaults to:</p><pre><code>
22239      * {top:0, right:0, bottom:0, left:0}
22240      * </code></pre>
22241      */
22242     defaultMargins : {left:0,top:0,right:0,bottom:0},
22243     /**
22244      * @cfg {String} padding
22245      * <p>Sets the padding to be applied to all child items managed by this layout.</p>
22246      * <p>This property must be specified as a string containing
22247      * space-separated, numeric padding values. The order of the sides associated
22248      * with each value matches the way CSS processes padding values:</p>
22249      * <div class="mdetail-params"><ul>
22250      * <li>If there is only one value, it applies to all sides.</li>
22251      * <li>If there are two values, the top and bottom borders are set to the
22252      * first value and the right and left are set to the second.</li>
22253      * <li>If there are three values, the top is set to the first value, the left
22254      * and right are set to the second, and the bottom is set to the third.</li>
22255      * <li>If there are four values, they apply to the top, right, bottom, and
22256      * left, respectively.</li>
22257      * </ul></div>
22258      * <p>Defaults to: <code>"0"</code></p>
22259      */
22260     padding : '0',
22261     // documented in subclasses
22262     pack : 'start',
22263
22264     // private
22265     monitorResize : true,
22266     type: 'box',
22267     scrollOffset : 0,
22268     extraCls : 'x-box-item',
22269     targetCls : 'x-box-layout-ct',
22270     innerCls : 'x-box-inner',
22271
22272     constructor : function(config){
22273         Ext.layout.BoxLayout.superclass.constructor.call(this, config);
22274
22275         if (Ext.isString(this.defaultMargins)) {
22276             this.defaultMargins = this.parseMargins(this.defaultMargins);
22277         }
22278     },
22279
22280     /**
22281      * @private
22282      * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
22283      * when laying out
22284      */
22285     onLayout: function(container, target) {
22286         Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target);
22287
22288         var items = this.getVisibleItems(container),
22289             tSize = this.getLayoutTargetSize();
22290
22291         /**
22292          * @private
22293          * @property layoutTargetLastSize
22294          * @type Object
22295          * Private cache of the last measured size of the layout target. This should never be used except by
22296          * BoxLayout subclasses during their onLayout run.
22297          */
22298         this.layoutTargetLastSize = tSize;
22299
22300         /**
22301          * @private
22302          * @property childBoxCache
22303          * @type Array
22304          * Array of the last calculated height, width, top and left positions of each visible rendered component
22305          * within the Box layout.
22306          */
22307         this.childBoxCache = this.calculateChildBoxes(items, tSize);
22308
22309         this.updateInnerCtSize(tSize, this.childBoxCache);
22310         this.updateChildBoxes(this.childBoxCache.boxes);
22311
22312         // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
22313         this.handleTargetOverflow(tSize, container, target);
22314     },
22315
22316     /**
22317      * Resizes and repositions each child component
22318      * @param {Array} boxes The box measurements
22319      */
22320     updateChildBoxes: function(boxes) {
22321         for (var i = 0, length = boxes.length; i < length; i++) {
22322             var box  = boxes[i],
22323                 comp = box.component;
22324
22325             if (box.dirtySize) {
22326                 comp.setSize(box.width, box.height);
22327             }
22328             // Don't set positions to NaN
22329             if (isNaN(box.left) || isNaN(box.top)) {
22330                 continue;
22331             }
22332             comp.setPosition(box.left, box.top);
22333         }
22334     },
22335
22336     /**
22337      * @private
22338      * Called by onRender just before the child components are sized and positioned. This resizes the innerCt
22339      * to make sure all child items fit within it. We call this before sizing the children because if our child
22340      * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them
22341      * again immediately afterwards, giving a performance hit.
22342      * Subclasses should provide an implementation.
22343      * @param {Object} currentSize The current height and width of the innerCt
22344      * @param {Array} calculations The new box calculations of all items to be laid out
22345      */
22346     updateInnerCtSize: Ext.emptyFn,
22347
22348     /**
22349      * @private
22350      * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden',
22351      * we need to lay out a second time because the scrollbars may have modified the height and width of the layout
22352      * target. Having a Box layout inside such a target is therefore not recommended.
22353      * @param {Object} previousTargetSize The size and height of the layout target before we just laid out
22354      * @param {Ext.Container} container The container
22355      * @param {Ext.Element} target The target element
22356      */
22357     handleTargetOverflow: function(previousTargetSize, container, target) {
22358         var overflow = target.getStyle('overflow');
22359
22360         if (overflow && overflow != 'hidden' &&!this.adjustmentPass) {
22361             var newTargetSize = this.getLayoutTargetSize();
22362             if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){
22363                 this.adjustmentPass = true;
22364                 this.onLayout(container, target);
22365             }
22366         }
22367
22368         delete this.adjustmentPass;
22369     },
22370
22371     // private
22372     isValidParent : function(c, target){
22373         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
22374     },
22375
22376     /**
22377      * @private
22378      * Returns all items that are both rendered and visible
22379      * @return {Array} All matching items
22380      */
22381     getVisibleItems: function(ct) {
22382         var ct  = ct || this.container,
22383             t   = ct.getLayoutTarget(),
22384             cti = ct.items.items,
22385             len = cti.length,
22386
22387             i, c, items = [];
22388
22389         for (i = 0; i < len; i++) {
22390             if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true  && c.collapsed !== true){
22391                 items.push(c);
22392             }
22393         }
22394
22395         return items;
22396     },
22397
22398     // private
22399     renderAll : function(ct, target){
22400         if(!this.innerCt){
22401             // the innerCt prevents wrapping and shuffling while
22402             // the container is resizing
22403             this.innerCt = target.createChild({cls:this.innerCls});
22404             this.padding = this.parseMargins(this.padding);
22405         }
22406         Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
22407     },
22408
22409     getLayoutTargetSize : function(){
22410         var target = this.container.getLayoutTarget(), ret;
22411         if (target) {
22412             ret = target.getViewSize();
22413
22414             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
22415             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
22416             // with getViewSize
22417             if (Ext.isIE && Ext.isStrict && ret.width == 0){
22418                 ret =  target.getStyleSize();
22419             }
22420
22421             ret.width -= target.getPadding('lr');
22422             ret.height -= target.getPadding('tb');
22423         }
22424         return ret;
22425     },
22426
22427     // private
22428     renderItem : function(c){
22429         if(Ext.isString(c.margins)){
22430             c.margins = this.parseMargins(c.margins);
22431         }else if(!c.margins){
22432             c.margins = this.defaultMargins;
22433         }
22434         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);
22435     }
22436 });
22437
22438 /**
22439  * @class Ext.layout.VBoxLayout
22440  * @extends Ext.layout.BoxLayout
22441  * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
22442  * space between child items containing a numeric <code>flex</code> configuration.</p>
22443  * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
22444  */
22445 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
22446     /**
22447      * @cfg {String} align
22448      * Controls how the child items of the container are aligned. Acceptable configuration values for this
22449      * property are:
22450      * <div class="mdetail-params"><ul>
22451      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
22452      * at the <b>left</b> side of the container</div></li>
22453      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
22454      * <b>mid-width</b> of the container</div></li>
22455      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
22456      * the width of the container</div></li>
22457      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
22458      * the size of the largest item.</div></li>
22459      * </ul></div>
22460      */
22461     align : 'left', // left, center, stretch, strechmax
22462     type: 'vbox',
22463
22464     /**
22465      * @cfg {String} pack
22466      * Controls how the child items of the container are packed together. Acceptable configuration values
22467      * for this property are:
22468      * <div class="mdetail-params"><ul>
22469      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
22470      * <b>top</b> side of container</div></li>
22471      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
22472      * <b>mid-height</b> of container</div></li>
22473      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>
22474      * side of container</div></li>
22475      * </ul></div>
22476      */
22477
22478     /**
22479      * @cfg {Number} flex
22480      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
22481      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>
22482      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
22483      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
22484      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
22485      */
22486
22487     /**
22488      * @private
22489      * See parent documentation
22490      */
22491     updateInnerCtSize: function(tSize, calcs) {
22492         var innerCtHeight = tSize.height,
22493             innerCtWidth  = calcs.meta.maxWidth + this.padding.left + this.padding.right;
22494
22495         if (this.align == 'stretch') {
22496             innerCtWidth = tSize.width;
22497         } else if (this.align == 'center') {
22498             innerCtWidth = Math.max(tSize.width, innerCtWidth);
22499         }
22500
22501         //we set the innerCt size first because if our child items are larger than the previous innerCt size
22502         //the browser will insert scrollbars and then remove them again immediately afterwards
22503         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
22504     },
22505
22506     /**
22507      * @private
22508      * Calculates the size and positioning of each item in the VBox. This iterates over all of the rendered,
22509      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
22510      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
22511      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
22512      * @param {Object} targetSize Object containing target size and height
22513      * @return {Object} Object containing box measurements for each child, plus meta data
22514      */
22515     calculateChildBoxes: function(visibleItems, targetSize) {
22516         var visibleCount = visibleItems.length,
22517
22518             padding      = this.padding,
22519             topOffset    = padding.top,
22520             leftOffset   = padding.left,
22521             paddingVert  = topOffset  + padding.bottom,
22522             paddingHoriz = leftOffset + padding.right,
22523
22524             width        = targetSize.width - this.scrollOffset,
22525             height       = targetSize.height,
22526             availWidth   = Math.max(0, width - paddingHoriz),
22527
22528             isStart      = this.pack == 'start',
22529             isCenter     = this.pack == 'center',
22530             isEnd        = this.pack == 'end',
22531
22532             nonFlexHeight= 0,
22533             maxWidth     = 0,
22534             totalFlex    = 0,
22535
22536             //used to cache the calculated size and position values for each child item
22537             boxes        = [],
22538
22539             //used in the for loops below, just declared here for brevity
22540             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedHeight, horizMargins, stretchWidth;
22541
22542             //gather the total flex of all flexed items and the width taken up by fixed width items
22543             for (i = 0; i < visibleCount; i++) {
22544                 child = visibleItems[i];
22545                 childHeight = child.height;
22546                 childWidth  = child.width;
22547                 canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
22548
22549
22550                 // Static height (numeric) requires no calcs
22551                 if (!Ext.isNumber(childHeight)) {
22552
22553                     // flex and not 'auto' height
22554                     if (child.flex && !childHeight) {
22555                         totalFlex += child.flex;
22556
22557                     // Not flexed or 'auto' height or undefined height
22558                     } else {
22559                         //Render and layout sub-containers without a flex or width defined, as otherwise we
22560                         //don't know how wide the sub-container should be and cannot calculate flexed widths
22561                         if (!childHeight && canLayout) {
22562                             child.doLayout();
22563                         }
22564
22565                         childSize = child.getSize();
22566                         childWidth = childSize.width;
22567                         childHeight = childSize.height;
22568                     }
22569                 }
22570
22571                 childMargins = child.margins;
22572
22573                 nonFlexHeight += (childHeight || 0) + childMargins.top + childMargins.bottom;
22574
22575                 // Max width for align - force layout of non-layed out subcontainers without a numeric width
22576                 if (!Ext.isNumber(childWidth)) {
22577                     if (canLayout) {
22578                         child.doLayout();
22579                     }
22580                     childWidth = child.getWidth();
22581                 }
22582
22583                 maxWidth = Math.max(maxWidth, childWidth + childMargins.left + childMargins.right);
22584
22585                 //cache the size of each child component
22586                 boxes.push({
22587                     component: child,
22588                     height   : childHeight || undefined,
22589                     width    : childWidth || undefined
22590                 });
22591             }
22592
22593             //the height available to the flexed items
22594             var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
22595
22596             if (isCenter) {
22597                 topOffset += availableHeight / 2;
22598             } else if (isEnd) {
22599                 topOffset += availableHeight;
22600             }
22601
22602             //temporary variables used in the flex height calculations below
22603             var remainingHeight = availableHeight,
22604                 remainingFlex   = totalFlex;
22605
22606             //calculate the height of each flexed item, and the left + top positions of every item
22607             for (i = 0; i < visibleCount; i++) {
22608                 child = visibleItems[i];
22609                 calcs = boxes[i];
22610
22611                 childMargins = child.margins;
22612                 horizMargins = childMargins.left + childMargins.right;
22613
22614                 topOffset   += childMargins.top;
22615
22616                 if (isStart && child.flex && !child.height) {
22617                     flexedHeight     = Math.ceil((child.flex / remainingFlex) * remainingHeight);
22618                     remainingHeight -= flexedHeight;
22619                     remainingFlex   -= child.flex;
22620
22621                     calcs.height = flexedHeight;
22622                     calcs.dirtySize = true;
22623                 }
22624
22625                 calcs.left = leftOffset + childMargins.left;
22626                 calcs.top  = topOffset;
22627
22628                 switch (this.align) {
22629                     case 'stretch':
22630                         stretchWidth = availWidth - horizMargins;
22631                         calcs.width  = stretchWidth.constrain(child.minHeight || 0, child.maxWidth || 1000000);
22632                         calcs.dirtySize = true;
22633                         break;
22634                     case 'stretchmax':
22635                         stretchWidth = maxWidth - horizMargins;
22636                         calcs.width  = stretchWidth.constrain(child.minHeight || 0, child.maxWidth || 1000000);
22637                         calcs.dirtySize = true;
22638                         break;
22639                     case 'center':
22640                         var diff = availWidth - calcs.width - horizMargins;
22641                         if (diff > 0) {
22642                             calcs.left = leftOffset + horizMargins + (diff / 2);
22643                         }
22644                 }
22645
22646                 topOffset += calcs.height + childMargins.bottom;
22647             }
22648
22649         return {
22650             boxes: boxes,
22651             meta : {
22652                 maxWidth: maxWidth
22653             }
22654         };
22655     }
22656 });
22657
22658 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;
22659
22660 /**
22661  * @class Ext.layout.HBoxLayout
22662  * @extends Ext.layout.BoxLayout
22663  * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
22664  * space between child items containing a numeric <code>flex</code> configuration.</p>
22665  * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
22666  */
22667 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
22668     /**
22669      * @cfg {String} align
22670      * Controls how the child items of the container are aligned. Acceptable configuration values for this
22671      * property are:
22672      * <div class="mdetail-params"><ul>
22673      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
22674      * at the <b>top</b> of the container</div></li>
22675      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
22676      * <b>middle</b> of the container</div></li>
22677      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
22678      * the height of the container</div></li>
22679      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
22680      * the height of the largest item.</div></li>
22681      */
22682     align: 'top', // top, middle, stretch, strechmax
22683
22684     type : 'hbox',
22685
22686     /**
22687      * @private
22688      * See parent documentation
22689      */
22690     updateInnerCtSize: function(tSize, calcs) {
22691         var innerCtWidth  = tSize.width,
22692             innerCtHeight = calcs.meta.maxHeight + this.padding.top + this.padding.bottom;
22693
22694         if (this.align == 'stretch') {
22695             innerCtHeight = tSize.height;
22696         } else if (this.align == 'middle') {
22697             innerCtHeight = Math.max(tSize.height, innerCtHeight);
22698         }
22699
22700         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
22701     },
22702
22703     /**
22704      * @cfg {String} pack
22705      * Controls how the child items of the container are packed together. Acceptable configuration values
22706      * for this property are:
22707      * <div class="mdetail-params"><ul>
22708      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
22709      * <b>left</b> side of container</div></li>
22710      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
22711      * <b>mid-width</b> of container</div></li>
22712      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
22713      * side of container</div></li>
22714      * </ul></div>
22715      */
22716     /**
22717      * @cfg {Number} flex
22718      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
22719      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
22720      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
22721      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
22722      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
22723      */
22724
22725     /**
22726      * @private
22727      * Calculates the size and positioning of each item in the HBox. This iterates over all of the rendered,
22728      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
22729      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
22730      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
22731      * @param {Object} targetSize Object containing target size and height
22732      * @return {Object} Object containing box measurements for each child, plus meta data
22733      */
22734     calculateChildBoxes: function(visibleItems, targetSize) {
22735         var visibleCount = visibleItems.length,
22736
22737             padding      = this.padding,
22738             topOffset    = padding.top,
22739             leftOffset   = padding.left,
22740             paddingVert  = topOffset  + padding.bottom,
22741             paddingHoriz = leftOffset + padding.right,
22742
22743             width        = targetSize.width - this.scrollOffset,
22744             height       = targetSize.height,
22745             availHeight  = Math.max(0, height - paddingVert),
22746
22747             isStart      = this.pack == 'start',
22748             isCenter     = this.pack == 'center',
22749             isEnd        = this.pack == 'end',
22750             // isRestore    = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
22751
22752             nonFlexWidth = 0,
22753             maxHeight    = 0,
22754             totalFlex    = 0,
22755
22756             //used to cache the calculated size and position values for each child item
22757             boxes        = [],
22758
22759             //used in the for loops below, just declared here for brevity
22760             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth, vertMargins, stretchHeight;
22761
22762             //gather the total flex of all flexed items and the width taken up by fixed width items
22763             for (i = 0; i < visibleCount; i++) {
22764                 child       = visibleItems[i];
22765                 childHeight = child.height;
22766                 childWidth  = child.width;
22767                 canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
22768
22769                 // Static width (numeric) requires no calcs
22770                 if (!Ext.isNumber(childWidth)) {
22771
22772                     // flex and not 'auto' width
22773                     if (child.flex && !childWidth) {
22774                         totalFlex += child.flex;
22775
22776                     // Not flexed or 'auto' width or undefined width
22777                     } else {
22778                         //Render and layout sub-containers without a flex or width defined, as otherwise we
22779                         //don't know how wide the sub-container should be and cannot calculate flexed widths
22780                         if (!childWidth && canLayout) {
22781                             child.doLayout();
22782                         }
22783
22784                         childSize   = child.getSize();
22785                         childWidth  = childSize.width;
22786                         childHeight = childSize.height;
22787                     }
22788                 }
22789
22790                 childMargins = child.margins;
22791
22792                 nonFlexWidth += (childWidth || 0) + childMargins.left + childMargins.right;
22793
22794                 // Max height for align - force layout of non-layed out subcontainers without a numeric height
22795                 if (!Ext.isNumber(childHeight)) {
22796                     if (canLayout) {
22797                         child.doLayout();
22798                     }
22799                     childHeight = child.getHeight();
22800                 }
22801
22802                 maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
22803
22804                 //cache the size of each child component
22805                 boxes.push({
22806                     component: child,
22807                     height   : childHeight || undefined,
22808                     width    : childWidth || undefined
22809                 });
22810             }
22811
22812             //the width available to the flexed items
22813             var availableWidth = Math.max(0, (width - nonFlexWidth - paddingHoriz));
22814
22815             if (isCenter) {
22816                 leftOffset += availableWidth / 2;
22817             } else if (isEnd) {
22818                 leftOffset += availableWidth;
22819             }
22820
22821             //temporary variables used in the flex width calculations below
22822             var remainingWidth = availableWidth,
22823                 remainingFlex  = totalFlex;
22824
22825             //calculate the widths of each flexed item, and the left + top positions of every item
22826             for (i = 0; i < visibleCount; i++) {
22827                 child = visibleItems[i];
22828                 calcs = boxes[i];
22829
22830                 childMargins = child.margins;
22831                 vertMargins  = childMargins.top + childMargins.bottom;
22832
22833                 leftOffset  += childMargins.left;
22834
22835                 if (isStart && child.flex && !child.width) {
22836                     flexedWidth     = Math.ceil((child.flex / remainingFlex) * remainingWidth);
22837                     remainingWidth -= flexedWidth;
22838                     remainingFlex  -= child.flex;
22839
22840                     calcs.width = flexedWidth;
22841                     calcs.dirtySize = true;
22842                 }
22843
22844                 calcs.left = leftOffset;
22845                 calcs.top  = topOffset + childMargins.top;
22846
22847                 switch (this.align) {
22848                     case 'stretch':
22849                         stretchHeight = availHeight - vertMargins;
22850                         calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
22851                         calcs.dirtySize = true;
22852                         break;
22853                     case 'stretchmax':
22854                         stretchHeight = maxHeight - vertMargins;
22855                         calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
22856                         calcs.dirtySize = true;
22857                         break;
22858                     case 'middle':
22859                         var diff = availHeight - calcs.height - vertMargins;
22860                         if (diff > 0) {
22861                             calcs.top = topOffset + vertMargins + (diff / 2);
22862                         }
22863                 }
22864                 leftOffset += calcs.width + childMargins.right;
22865             }
22866
22867         return {
22868             boxes: boxes,
22869             meta : {
22870                 maxHeight: maxHeight
22871             }
22872         };
22873     }
22874 });
22875
22876 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;
22877 /**
22878  * @class Ext.layout.ToolbarLayout
22879  * @extends Ext.layout.ContainerLayout
22880  * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not
22881  * usually be used by any other class.
22882  */
22883 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
22884     monitorResize : true,
22885
22886     type: 'toolbar',
22887
22888     /**
22889      * @property triggerWidth
22890      * @type Number
22891      * The width allocated for the menu trigger at the extreme right end of the Toolbar
22892      */
22893     triggerWidth: 18,
22894
22895     /**
22896      * @property noItemsMenuText
22897      * @type String
22898      * HTML fragment to render into the toolbar overflow menu if there are no items to display
22899      */
22900     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
22901
22902     /**
22903      * @private
22904      * @property lastOverflow
22905      * @type Boolean
22906      * Used internally to record whether the last layout caused an overflow or not
22907      */
22908     lastOverflow: false,
22909
22910     /**
22911      * @private
22912      * @property tableHTML
22913      * @type String
22914      * String used to build the HTML injected to support the Toolbar's layout. The align property is
22915      * injected into this string inside the td.x-toolbar-left element during onLayout.
22916      */
22917     tableHTML: [
22918         '<table cellspacing="0" class="x-toolbar-ct">',
22919             '<tbody>',
22920                 '<tr>',
22921                     '<td class="x-toolbar-left" align="{0}">',
22922                         '<table cellspacing="0">',
22923                             '<tbody>',
22924                                 '<tr class="x-toolbar-left-row"></tr>',
22925                             '</tbody>',
22926                         '</table>',
22927                     '</td>',
22928                     '<td class="x-toolbar-right" align="right">',
22929                         '<table cellspacing="0" class="x-toolbar-right-ct">',
22930                             '<tbody>',
22931                                 '<tr>',
22932                                     '<td>',
22933                                         '<table cellspacing="0">',
22934                                             '<tbody>',
22935                                                 '<tr class="x-toolbar-right-row"></tr>',
22936                                             '</tbody>',
22937                                         '</table>',
22938                                     '</td>',
22939                                     '<td>',
22940                                         '<table cellspacing="0">',
22941                                             '<tbody>',
22942                                                 '<tr class="x-toolbar-extras-row"></tr>',
22943                                             '</tbody>',
22944                                         '</table>',
22945                                     '</td>',
22946                                 '</tr>',
22947                             '</tbody>',
22948                         '</table>',
22949                     '</td>',
22950                 '</tr>',
22951             '</tbody>',
22952         '</table>'
22953     ].join(""),
22954
22955     /**
22956      * @private
22957      * Create the wrapping Toolbar HTML and render/move all the items into the correct places
22958      */
22959     onLayout : function(ct, target) {
22960         //render the Toolbar <table> HTML if it's not already present
22961         if (!this.leftTr) {
22962             var align = ct.buttonAlign == 'center' ? 'center' : 'left';
22963
22964             target.addClass('x-toolbar-layout-ct');
22965             target.insertHtml('beforeEnd', String.format(this.tableHTML, align));
22966
22967             this.leftTr   = target.child('tr.x-toolbar-left-row', true);
22968             this.rightTr  = target.child('tr.x-toolbar-right-row', true);
22969             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);
22970
22971             if (this.hiddenItem == undefined) {
22972                 /**
22973                  * @property hiddenItems
22974                  * @type Array
22975                  * Holds all items that are currently hidden due to there not being enough space to render them
22976                  * These items will appear on the expand menu.
22977                  */
22978                 this.hiddenItems = [];
22979             }
22980         }
22981
22982         var side     = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
22983             items    = ct.items.items,
22984             position = 0;
22985
22986         //render each item if not already rendered, place it into the correct (left or right) target
22987         for (var i = 0, len = items.length, c; i < len; i++, position++) {
22988             c = items[i];
22989
22990             if (c.isFill) {
22991                 side   = this.rightTr;
22992                 position = -1;
22993             } else if (!c.rendered) {
22994                 c.render(this.insertCell(c, side, position));
22995             } else {
22996                 if (!c.xtbHidden && !this.isValidParent(c, side.childNodes[position])) {
22997                     var td = this.insertCell(c, side, position);
22998                     td.appendChild(c.getPositionEl().dom);
22999                     c.container = Ext.get(td);
23000                 }
23001             }
23002         }
23003
23004         //strip extra empty cells
23005         this.cleanup(this.leftTr);
23006         this.cleanup(this.rightTr);
23007         this.cleanup(this.extrasTr);
23008         this.fitToSize(target);
23009     },
23010
23011     /**
23012      * @private
23013      * Removes any empty nodes from the given element
23014      * @param {Ext.Element} el The element to clean up
23015      */
23016     cleanup : function(el) {
23017         var cn = el.childNodes, i, c;
23018
23019         for (i = cn.length-1; i >= 0 && (c = cn[i]); i--) {
23020             if (!c.firstChild) {
23021                 el.removeChild(c);
23022             }
23023         }
23024     },
23025
23026     /**
23027      * @private
23028      * Inserts the given Toolbar item into the given element
23029      * @param {Ext.Component} c The component to add
23030      * @param {Ext.Element} target The target to add the component to
23031      * @param {Number} position The position to add the component at
23032      */
23033     insertCell : function(c, target, position) {
23034         var td = document.createElement('td');
23035         td.className = 'x-toolbar-cell';
23036
23037         target.insertBefore(td, target.childNodes[position] || null);
23038
23039         return td;
23040     },
23041
23042     /**
23043      * @private
23044      * Hides an item because it will not fit in the available width. The item will be unhidden again
23045      * if the Toolbar is resized to be large enough to show it
23046      * @param {Ext.Component} item The item to hide
23047      */
23048     hideItem : function(item) {
23049         this.hiddenItems.push(item);
23050
23051         item.xtbHidden = true;
23052         item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
23053         item.hide();
23054     },
23055
23056     /**
23057      * @private
23058      * Unhides an item that was previously hidden due to there not being enough space left on the Toolbar
23059      * @param {Ext.Component} item The item to show
23060      */
23061     unhideItem : function(item) {
23062         item.show();
23063         item.xtbHidden = false;
23064         this.hiddenItems.remove(item);
23065     },
23066
23067     /**
23068      * @private
23069      * Returns the width of the given toolbar item. If the item is currently hidden because there
23070      * is not enough room to render it, its previous width is returned
23071      * @param {Ext.Component} c The component to measure
23072      * @return {Number} The width of the item
23073      */
23074     getItemWidth : function(c) {
23075         return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
23076     },
23077
23078     /**
23079      * @private
23080      * Called at the end of onLayout. At this point the Toolbar has already been resized, so we need
23081      * to fit the items into the available width. We add up the width required by all of the items in
23082      * the toolbar - if we don't have enough space we hide the extra items and render the expand menu
23083      * trigger.
23084      * @param {Ext.Element} target The Element the Toolbar is currently laid out within
23085      */
23086     fitToSize : function(target) {
23087         if (this.container.enableOverflow === false) {
23088             return;
23089         }
23090
23091         var width       = target.dom.clientWidth,
23092             tableWidth  = target.dom.firstChild.offsetWidth,
23093             clipWidth   = width - this.triggerWidth,
23094             lastWidth   = this.lastWidth || 0,
23095
23096             hiddenItems = this.hiddenItems,
23097             hasHiddens  = hiddenItems.length != 0,
23098             isLarger    = width >= lastWidth;
23099
23100         this.lastWidth  = width;
23101
23102         if (tableWidth > width || (hasHiddens && isLarger)) {
23103             var items     = this.container.items.items,
23104                 len       = items.length,
23105                 loopWidth = 0,
23106                 item;
23107
23108             for (var i = 0; i < len; i++) {
23109                 item = items[i];
23110
23111                 if (!item.isFill) {
23112                     loopWidth += this.getItemWidth(item);
23113                     if (loopWidth > clipWidth) {
23114                         if (!(item.hidden || item.xtbHidden)) {
23115                             this.hideItem(item);
23116                         }
23117                     } else if (item.xtbHidden) {
23118                         this.unhideItem(item);
23119                     }
23120                 }
23121             }
23122         }
23123
23124         //test for number of hidden items again here because they may have changed above
23125         hasHiddens = hiddenItems.length != 0;
23126
23127         if (hasHiddens) {
23128             this.initMore();
23129
23130             if (!this.lastOverflow) {
23131                 this.container.fireEvent('overflowchange', this.container, true);
23132                 this.lastOverflow = true;
23133             }
23134         } else if (this.more) {
23135             this.clearMenu();
23136             this.more.destroy();
23137             delete this.more;
23138
23139             if (this.lastOverflow) {
23140                 this.container.fireEvent('overflowchange', this.container, false);
23141                 this.lastOverflow = false;
23142             }
23143         }
23144     },
23145
23146     /**
23147      * @private
23148      * Returns a menu config for a given component. This config is used to create a menu item
23149      * to be added to the expander menu
23150      * @param {Ext.Component} component The component to create the config for
23151      * @param {Boolean} hideOnClick Passed through to the menu item
23152      */
23153     createMenuConfig : function(component, hideOnClick){
23154         var config = Ext.apply({}, component.initialConfig),
23155             group  = component.toggleGroup;
23156
23157         Ext.copyTo(config, component, [
23158             'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
23159         ]);
23160
23161         Ext.apply(config, {
23162             text       : component.overflowText || component.text,
23163             hideOnClick: hideOnClick
23164         });
23165
23166         if (group || component.enableToggle) {
23167             Ext.apply(config, {
23168                 group  : group,
23169                 checked: component.pressed,
23170                 listeners: {
23171                     checkchange: function(item, checked){
23172                         component.toggle(checked);
23173                     }
23174                 }
23175             });
23176         }
23177
23178         delete config.ownerCt;
23179         delete config.xtype;
23180         delete config.id;
23181
23182         return config;
23183     },
23184
23185     /**
23186      * @private
23187      * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
23188      * @param {Ext.menu.Menu} menu The menu to add to
23189      * @param {Ext.Component} component The component to add
23190      */
23191     addComponentToMenu : function(menu, component) {
23192         if (component instanceof Ext.Toolbar.Separator) {
23193             menu.add('-');
23194
23195         } else if (Ext.isFunction(component.isXType)) {
23196             if (component.isXType('splitbutton')) {
23197                 menu.add(this.createMenuConfig(component, true));
23198
23199             } else if (component.isXType('button')) {
23200                 menu.add(this.createMenuConfig(component, !component.menu));
23201
23202             } else if (component.isXType('buttongroup')) {
23203                 component.items.each(function(item){
23204                      this.addComponentToMenu(menu, item);
23205                 }, this);
23206             }
23207         }
23208     },
23209
23210     /**
23211      * @private
23212      * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
23213      * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
23214      */
23215     clearMenu : function(){
23216         var menu = this.moreMenu;
23217         if (menu && menu.items) {
23218             menu.items.each(function(item){
23219                 delete item.menu;
23220             });
23221         }
23222     },
23223
23224     /**
23225      * @private
23226      * Called before the expand menu is shown, this rebuilds the menu since it was last shown because
23227      * it is possible that the items hidden due to space limitations on the Toolbar have changed since.
23228      * @param {Ext.menu.Menu} m The menu
23229      */
23230     beforeMoreShow : function(menu) {
23231         var items = this.container.items.items,
23232             len   = items.length,
23233             item,
23234             prev;
23235
23236         var needsSep = function(group, item){
23237             return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
23238         };
23239
23240         this.clearMenu();
23241         menu.removeAll();
23242         for (var i = 0; i < len; i++) {
23243             item = items[i];
23244             if (item.xtbHidden) {
23245                 if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
23246                     menu.add('-');
23247                 }
23248                 this.addComponentToMenu(menu, item);
23249                 prev = item;
23250             }
23251         }
23252
23253         // put something so the menu isn't empty if no compatible items found
23254         if (menu.items.length < 1) {
23255             menu.add(this.noItemsMenuText);
23256         }
23257     },
23258
23259     /**
23260      * @private
23261      * Creates the expand trigger and menu, adding them to the <tr> at the extreme right of the
23262      * Toolbar table
23263      */
23264     initMore : function(){
23265         if (!this.more) {
23266             /**
23267              * @private
23268              * @property moreMenu
23269              * @type Ext.menu.Menu
23270              * The expand menu - holds items for every Toolbar item that cannot be shown
23271              * because the Toolbar is currently not wide enough.
23272              */
23273             this.moreMenu = new Ext.menu.Menu({
23274                 ownerCt : this.container,
23275                 listeners: {
23276                     beforeshow: this.beforeMoreShow,
23277                     scope: this
23278                 }
23279             });
23280
23281             /**
23282              * @private
23283              * @property more
23284              * @type Ext.Button
23285              * The expand button which triggers the overflow menu to be shown
23286              */
23287             this.more = new Ext.Button({
23288                 iconCls: 'x-toolbar-more-icon',
23289                 cls    : 'x-toolbar-more',
23290                 menu   : this.moreMenu,
23291                 ownerCt: this.container
23292             });
23293
23294             var td = this.insertCell(this.more, this.extrasTr, 100);
23295             this.more.render(td);
23296         }
23297     },
23298
23299     destroy : function(){
23300         Ext.destroy(this.more, this.moreMenu);
23301         delete this.leftTr;
23302         delete this.rightTr;
23303         delete this.extrasTr;
23304         Ext.layout.ToolbarLayout.superclass.destroy.call(this);
23305     }
23306 });
23307
23308 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;
23309 /**
23310  * @class Ext.layout.MenuLayout
23311  * @extends Ext.layout.ContainerLayout
23312  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>
23313  */
23314  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {
23315     monitorResize : true,
23316
23317     type: 'menu',
23318
23319     setContainer : function(ct){
23320         this.monitorResize = !ct.floating;
23321         // This event is only fired by the menu in IE, used so we don't couple
23322         // the menu with the layout.
23323         ct.on('autosize', this.doAutoSize, this);
23324         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);
23325     },
23326
23327     renderItem : function(c, position, target){
23328         if (!this.itemTpl) {
23329             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(
23330                 '<li id="{itemId}" class="{itemCls}">',
23331                     '<tpl if="needsIcon">',
23332                         '<img src="{icon}" class="{iconCls}"/>',
23333                     '</tpl>',
23334                 '</li>'
23335             );
23336         }
23337
23338         if(c && !c.rendered){
23339             if(Ext.isNumber(position)){
23340                 position = target.dom.childNodes[position];
23341             }
23342             var a = this.getItemArgs(c);
23343
23344 //          The Component's positionEl is the <li> it is rendered into
23345             c.render(c.positionEl = position ?
23346                 this.itemTpl.insertBefore(position, a, true) :
23347                 this.itemTpl.append(target, a, true));
23348
23349 //          Link the containing <li> to the item.
23350             c.positionEl.menuItemId = c.getItemId();
23351
23352 //          If rendering a regular Component, and it needs an icon,
23353 //          move the Component rightwards.
23354             if (!a.isMenuItem && a.needsIcon) {
23355                 c.positionEl.addClass('x-menu-list-item-indent');
23356             }
23357             this.configureItem(c, position);
23358         }else if(c && !this.isValidParent(c, target)){
23359             if(Ext.isNumber(position)){
23360                 position = target.dom.childNodes[position];
23361             }
23362             target.dom.insertBefore(c.getActionEl().dom, position || null);
23363         }
23364     },
23365
23366     getItemArgs : function(c) {
23367         var isMenuItem = c instanceof Ext.menu.Item;
23368         return {
23369             isMenuItem: isMenuItem,
23370             needsIcon: !isMenuItem && (c.icon || c.iconCls),
23371             icon: c.icon || Ext.BLANK_IMAGE_URL,
23372             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),
23373             itemId: 'x-menu-el-' + c.id,
23374             itemCls: 'x-menu-list-item '
23375         };
23376     },
23377
23378     //  Valid if the Component is in a <li> which is part of our target <ul>
23379     isValidParent : function(c, target) {
23380         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);
23381     },
23382
23383     onLayout : function(ct, target){
23384         Ext.layout.MenuLayout.superclass.onLayout.call(this, ct, target);
23385         this.doAutoSize();
23386     },
23387
23388     doAutoSize : function(){
23389         var ct = this.container, w = ct.width;
23390         if(ct.floating){
23391             if(w){
23392                 ct.setWidth(w);
23393             }else if(Ext.isIE){
23394                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);
23395                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc
23396                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));
23397             }
23398         }
23399     }
23400 });
23401 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;
23402 /**
23403  * @class Ext.Viewport
23404  * @extends Ext.Container
23405  * <p>A specialized container representing the viewable application area (the browser viewport).</p>
23406  * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of
23407  * the browser viewport and manages window resizing. There may only be one Viewport created
23408  * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s
23409  * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}
23410  * method of any of its child Panels may themselves have a layout.</p>
23411  * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide
23412  * for scrolling if needed using the {@link #autoScroll} config.</p>
23413  * <p>An example showing a classic application border layout:</p><pre><code>
23414 new Ext.Viewport({
23415     layout: 'border',
23416     items: [{
23417         region: 'north',
23418         html: '&lt;h1 class="x-panel-header">Page Title&lt;/h1>',
23419         autoHeight: true,
23420         border: false,
23421         margins: '0 0 5 0'
23422     }, {
23423         region: 'west',
23424         collapsible: true,
23425         title: 'Navigation',
23426         width: 200
23427         // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout}
23428     }, {
23429         region: 'south',
23430         title: 'Title for Panel',
23431         collapsible: true,
23432         html: 'Information goes here',
23433         split: true,
23434         height: 100,
23435         minHeight: 100
23436     }, {
23437         region: 'east',
23438         title: 'Title for the Grid Panel',
23439         collapsible: true,
23440         split: true,
23441         width: 200,
23442         xtype: 'grid',
23443         // remaining grid configuration not shown ...
23444         // notice that the GridPanel is added directly as the region
23445         // it is not "overnested" inside another Panel
23446     }, {
23447         region: 'center',
23448         xtype: 'tabpanel', // TabPanel itself has no title
23449         items: {
23450             title: 'Default Tab',
23451             html: 'The first tab\'s content. Others may be added dynamically'
23452         }
23453     }]
23454 });
23455 </code></pre>
23456  * @constructor
23457  * Create a new Viewport
23458  * @param {Object} config The config object
23459  * @xtype viewport
23460  */
23461 Ext.Viewport = Ext.extend(Ext.Container, {
23462     /*
23463      * Privatize config options which, if used, would interfere with the
23464      * correct operation of the Viewport as the sole manager of the
23465      * layout of the document body.
23466      */
23467     /**
23468      * @cfg {Mixed} applyTo @hide
23469      */
23470     /**
23471      * @cfg {Boolean} allowDomMove @hide
23472      */
23473     /**
23474      * @cfg {Boolean} hideParent @hide
23475      */
23476     /**
23477      * @cfg {Mixed} renderTo @hide
23478      */
23479     /**
23480      * @cfg {Boolean} hideParent @hide
23481      */
23482     /**
23483      * @cfg {Number} height @hide
23484      */
23485     /**
23486      * @cfg {Number} width @hide
23487      */
23488     /**
23489      * @cfg {Boolean} autoHeight @hide
23490      */
23491     /**
23492      * @cfg {Boolean} autoWidth @hide
23493      */
23494     /**
23495      * @cfg {Boolean} deferHeight @hide
23496      */
23497     /**
23498      * @cfg {Boolean} monitorResize @hide
23499      */
23500
23501     initComponent : function() {
23502         Ext.Viewport.superclass.initComponent.call(this);
23503         document.getElementsByTagName('html')[0].className += ' x-viewport';
23504         this.el = Ext.getBody();
23505         this.el.setHeight = Ext.emptyFn;
23506         this.el.setWidth = Ext.emptyFn;
23507         this.el.setSize = Ext.emptyFn;
23508         this.el.dom.scroll = 'no';
23509         this.allowDomMove = false;
23510         this.autoWidth = true;
23511         this.autoHeight = true;
23512         Ext.EventManager.onWindowResize(this.fireResize, this);
23513         this.renderTo = this.el;
23514     },
23515
23516     fireResize : function(w, h){
23517         this.fireEvent('resize', this, w, h, w, h);
23518     }
23519 });
23520 Ext.reg('viewport', Ext.Viewport);
23521 /**
23522  * @class Ext.Panel
23523  * @extends Ext.Container
23524  * <p>Panel is a container that has specific functionality and structural components that make
23525  * it the perfect building block for application-oriented user interfaces.</p>
23526  * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
23527  * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
23528  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
23529  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
23530  * those child elements need to be sized using one of Ext's built-in <code><b>{@link Ext.Container#layout layout}</b></code> schemes. By
23531  * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
23532  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
23533  * at all.</p>
23534  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
23535  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
23536  * information).</p>
23537  * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
23538  * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
23539  * behavior.  Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
23540  * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
23541  * @constructor
23542  * @param {Object} config The config object
23543  * @xtype panel
23544  */
23545 Ext.Panel = Ext.extend(Ext.Container, {
23546     /**
23547      * The Panel's header {@link Ext.Element Element}. Read-only.
23548      * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
23549      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
23550      * @type Ext.Element
23551      * @property header
23552      */
23553     /**
23554      * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
23555      * The content may be specified in the {@link #html} config, or it may be loaded using the
23556      * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
23557      * <p>If this is used to load visible HTML elements in either way, then
23558      * the Panel may not be used as a Layout for hosting nested Panels.</p>
23559      * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
23560      * then the body Element must not be loaded or changed - it is under the control
23561      * of the Panel's Layout.
23562      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
23563      * @type Ext.Element
23564      * @property body
23565      */
23566     /**
23567      * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
23568      * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
23569      * @type Ext.Element
23570      * @property bwrap
23571      */
23572     /**
23573      * True if this panel is collapsed. Read-only.
23574      * @type Boolean
23575      * @property collapsed
23576      */
23577     /**
23578      * @cfg {Object} bodyCfg
23579      * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
23580      * Panel Element.</p>
23581      * <p>By default, the Default element in the table below will be used for the html markup to
23582      * create a child element with the commensurate Default class name (<code>baseCls</code> will be
23583      * replaced by <code>{@link #baseCls}</code>):</p>
23584      * <pre>
23585      * Panel      Default  Default             Custom      Additional       Additional
23586      * Element    element  class               element     class            style
23587      * ========   ==========================   =========   ==============   ===========
23588      * {@link #header}     div      {@link #baseCls}+'-header'   {@link #headerCfg}   headerCssClass   headerStyle
23589      * {@link #bwrap}      div      {@link #baseCls}+'-bwrap'     {@link #bwrapCfg}    bwrapCssClass    bwrapStyle
23590      * + tbar     div      {@link #baseCls}+'-tbar'       {@link #tbarCfg}     tbarCssClass     tbarStyle
23591      * + {@link #body}     div      {@link #baseCls}+'-body'       {@link #bodyCfg}     {@link #bodyCssClass}     {@link #bodyStyle}
23592      * + bbar     div      {@link #baseCls}+'-bbar'       {@link #bbarCfg}     bbarCssClass     bbarStyle
23593      * + {@link #footer}   div      {@link #baseCls}+'-footer'   {@link #footerCfg}   footerCssClass   footerStyle
23594      * </pre>
23595      * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
23596      * to use a different form of markup than is created by default. An example of this might be
23597      * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
23598      * a header, or forcing centering of all Panel content by having the body be a &lt;center&gt;
23599      * element:</p>
23600      * <pre><code>
23601 new Ext.Panel({
23602     title: 'Message Title',
23603     renderTo: Ext.getBody(),
23604     width: 200, height: 130,
23605     <b>bodyCfg</b>: {
23606         tag: 'center',
23607         cls: 'x-panel-body',  // Default class not applied if Custom element specified
23608         html: 'Message'
23609     },
23610     footerCfg: {
23611         tag: 'h2',
23612         cls: 'x-panel-footer'        // same as the Default class
23613         html: 'footer html'
23614     },
23615     footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
23616     footerStyle:    'background-color:red' // see {@link #bodyStyle}
23617 });
23618      * </code></pre>
23619      * <p>The example above also explicitly creates a <code>{@link #footer}</code> with custom markup and
23620      * styling applied.</p>
23621      */
23622     /**
23623      * @cfg {Object} headerCfg
23624      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23625      * of this Panel's {@link #header} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23626      */
23627     /**
23628      * @cfg {Object} bwrapCfg
23629      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23630      * of this Panel's {@link #bwrap} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23631      */
23632     /**
23633      * @cfg {Object} tbarCfg
23634      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23635      * of this Panel's {@link #tbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23636      */
23637     /**
23638      * @cfg {Object} bbarCfg
23639      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23640      * of this Panel's {@link #bbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23641      */
23642     /**
23643      * @cfg {Object} footerCfg
23644      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23645      * of this Panel's {@link #footer} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23646      */
23647     /**
23648      * @cfg {Boolean} closable
23649      * Panels themselves do not directly support being closed, but some Panel subclasses do (like
23650      * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}.  Specify <code>true</code>
23651      * to enable closing in such situations. Defaults to <code>false</code>.
23652      */
23653     /**
23654      * The Panel's footer {@link Ext.Element Element}. Read-only.
23655      * <p>This Element is used to house the Panel's <code>{@link #buttons}</code> or <code>{@link #fbar}</code>.</p>
23656      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
23657      * @type Ext.Element
23658      * @property footer
23659      */
23660     /**
23661      * @cfg {Mixed} applyTo
23662      * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
23663      * the document that specifies some panel-specific structural markup.  When <code>applyTo</code> is used,
23664      * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
23665      * will automatically create those components from that markup. Any required components not specified in the
23666      * markup will be autogenerated if necessary.</p>
23667      * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
23668      * <ul><li>baseCls + '-header'</li>
23669      * <li>baseCls + '-header-text'</li>
23670      * <li>baseCls + '-bwrap'</li>
23671      * <li>baseCls + '-tbar'</li>
23672      * <li>baseCls + '-body'</li>
23673      * <li>baseCls + '-bbar'</li>
23674      * <li>baseCls + '-footer'</li></ul>
23675      * <p>Using this config, a call to render() is not required.  If applyTo is specified, any value passed for
23676      * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
23677      * panel's container.</p>
23678      */
23679     /**
23680      * @cfg {Object/Array} tbar
23681      * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
23682      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
23683      * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
23684      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23685      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23686      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23687      * submission parameters are collected from the DOM tree.</p>
23688      */
23689     /**
23690      * @cfg {Object/Array} bbar
23691      * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
23692      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
23693      * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
23694      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23695      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23696      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23697      * submission parameters are collected from the DOM tree.</p>
23698      */
23699     /** @cfg {Object/Array} fbar
23700      * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
23701      * {@link Ext.Button Button}s/{@link Ext.Button Button} configs, describing a {@link Ext.Toolbar Toolbar} to be rendered into this Panel's footer element.</p>
23702      * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
23703      * <p>If <code>{@link #buttons}</code> are specified, they will supersede the <code>fbar</code> configuration property.</p>
23704      * The Panel's <code>{@link #buttonAlign}</code> configuration affects the layout of these items, for example:
23705      * <pre><code>
23706 var w = new Ext.Window({
23707     height: 250,
23708     width: 500,
23709     bbar: new Ext.Toolbar({
23710         items: [{
23711             text: 'bbar Left'
23712         },'->',{
23713             text: 'bbar Right'
23714         }]
23715     }),
23716     {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
23717                                   // to control the alignment of fbar items
23718     fbar: [{
23719         text: 'fbar Left'
23720     },'->',{
23721         text: 'fbar Right'
23722     }]
23723 }).show();
23724      * </code></pre>
23725      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23726      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23727      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23728      * submission parameters are collected from the DOM tree.</p>
23729      */
23730     /**
23731      * @cfg {Boolean} header
23732      * <code>true</code> to create the Panel's header element explicitly, <code>false</code> to skip creating
23733      * it.  If a <code>{@link #title}</code> is set the header will be created automatically, otherwise it will not.
23734      * If a <code>{@link #title}</code> is set but <code>header</code> is explicitly set to <code>false</code>, the header
23735      * will not be rendered.
23736      */
23737     /**
23738      * @cfg {Boolean} footer
23739      * <code>true</code> to create the footer element explicitly, false to skip creating it. The footer
23740      * will be created automatically if <code>{@link #buttons}</code> or a <code>{@link #fbar}</code> have
23741      * been configured.  See <code>{@link #bodyCfg}</code> for an example.
23742      */
23743     /**
23744      * @cfg {String} title
23745      * The title text to be used as innerHTML (html tags are accepted) to display in the panel
23746      * <code>{@link #header}</code> (defaults to ''). When a <code>title</code> is specified the
23747      * <code>{@link #header}</code> element will automatically be created and displayed unless
23748      * {@link #header} is explicitly set to <code>false</code>.  If you do not want to specify a
23749      * <code>title</code> at config time, but you may want one later, you must either specify a non-empty
23750      * <code>title</code> (a blank space ' ' will do) or <code>header:true</code> so that the container
23751      * element will get created.
23752      */
23753     /**
23754      * @cfg {Array} buttons
23755      * <code>buttons</code> will be used as <code>{@link Ext.Container#items items}</code> for the toolbar in
23756      * the footer (<code>{@link #fbar}</code>). Typically the value of this configuration property will be
23757      * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
23758      * If an item is configured with <code>minWidth</code> or the Panel is configured with <code>minButtonWidth</code>,
23759      * that width will be applied to the item.
23760      */
23761     /**
23762      * @cfg {Object/String/Function} autoLoad
23763      * A valid url spec according to the Updater {@link Ext.Updater#update} method.
23764      * If autoLoad is not null, the panel will attempt to load its contents
23765      * immediately upon render.<p>
23766      * The URL will become the default URL for this panel's {@link #body} element,
23767      * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
23768      */
23769     /**
23770      * @cfg {Boolean} frame
23771      * <code>false</code> by default to render with plain 1px square borders. <code>true</code> to render with
23772      * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
23773      * <p>The template generated for each condition is depicted below:</p><pre><code>
23774      *
23775 // frame = false
23776 &lt;div id="developer-specified-id-goes-here" class="x-panel">
23777
23778     &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:false)&lt;/span>&lt;/div>
23779
23780     &lt;div class="x-panel-bwrap">
23781         &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
23782     &lt;/div>
23783 &lt;/div>
23784
23785 // frame = true (create 9 elements)
23786 &lt;div id="developer-specified-id-goes-here" class="x-panel">
23787     &lt;div class="x-panel-tl">&lt;div class="x-panel-tr">&lt;div class="x-panel-tc">
23788         &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:true)&lt;/span>&lt;/div>
23789     &lt;/div>&lt;/div>&lt;/div>
23790
23791     &lt;div class="x-panel-bwrap">
23792         &lt;div class="x-panel-ml">&lt;div class="x-panel-mr">&lt;div class="x-panel-mc">
23793             &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
23794         &lt;/div>&lt;/div>&lt;/div>
23795
23796         &lt;div class="x-panel-bl">&lt;div class="x-panel-br">&lt;div class="x-panel-bc"/>
23797         &lt;/div>&lt;/div>&lt;/div>
23798 &lt;/div>
23799      * </code></pre>
23800      */
23801     /**
23802      * @cfg {Boolean} border
23803      * True to display the borders of the panel's body element, false to hide them (defaults to true).  By default,
23804      * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
23805      */
23806     /**
23807      * @cfg {Boolean} bodyBorder
23808      * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
23809      * This only applies when {@link #border} == true.  If border == true and bodyBorder == false, the border will display
23810      * as a 1px wide inset border, giving the entire body element an inset appearance.
23811      */
23812     /**
23813      * @cfg {String/Object/Function} bodyCssClass
23814      * Additional css class selector to be applied to the {@link #body} element in the format expected by
23815      * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
23816      */
23817     /**
23818      * @cfg {String/Object/Function} bodyStyle
23819      * Custom CSS styles to be applied to the {@link #body} element in the format expected by
23820      * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
23821      */
23822     /**
23823      * @cfg {String} iconCls
23824      * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
23825      * <p>An example of specifying a custom icon class would be something like:
23826      * </p><pre><code>
23827 // specify the property in the config for the class:
23828      ...
23829      iconCls: 'my-icon'
23830
23831 // css class that specifies background image to be used as the icon image:
23832 .my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
23833 </code></pre>
23834      */
23835     /**
23836      * @cfg {Boolean} collapsible
23837      * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
23838      * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
23839      */
23840     /**
23841      * @cfg {Array} tools
23842      * An array of tool button configs to be added to the header tool area. When rendered, each tool is
23843      * stored as an {@link Ext.Element Element} referenced by a public property called <code><b></b>tools.<i>&lt;tool-type&gt;</i></code>
23844      * <p>Each tool config may contain the following properties:
23845      * <div class="mdetail-params"><ul>
23846      * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
23847      * of tool to create. By default, this assigns a CSS class of the form <code>x-tool-<i>&lt;tool-type&gt;</i></code> to the
23848      * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
23849      * The developer may implement custom tools by supplying alternate CSS rules and background images:
23850      * <ul>
23851      * <div class="x-tool x-tool-toggle" style="float:left; margin-right:5;"> </div><div><code> toggle</code> (Created by default when {@link #collapsible} is <code>true</code>)</div>
23852      * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><code> close</code></div>
23853      * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><code> minimize</code></div>
23854      * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><code> maximize</code></div>
23855      * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><code> restore</code></div>
23856      * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><code> gear</code></div>
23857      * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><code> pin</code></div>
23858      * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><code> unpin</code></div>
23859      * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><code> right</code></div>
23860      * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><code> left</code></div>
23861      * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><code> up</code></div>
23862      * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><code> down</code></div>
23863      * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><code> refresh</code></div>
23864      * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><code> minus</code></div>
23865      * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><code> plus</code></div>
23866      * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><code> help</code></div>
23867      * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><code> search</code></div>
23868      * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><code> save</code></div>
23869      * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><code> print</code></div>
23870      * </ul></div></li>
23871      * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
23872      * call when clicked. Arguments passed are:<ul>
23873      * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
23874      * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
23875      * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
23876      * <li><b>tc</b> : Object<div class="sub-desc">The tool configuration object</div></li>
23877      * </ul></div></li>
23878      * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
23879      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
23880      * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
23881      * a config argument to {@link Ext.QuickTip#register}</div></li>
23882      * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
23883      * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
23884      * event listeners in the format of an argument to {@link #addListener}</div></li>
23885      * </ul></div>
23886      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
23887      * tools only provide the visual button. Any required functionality must be provided by adding
23888      * handlers that implement the necessary behavior.</p>
23889      * <p>Example usage:</p>
23890      * <pre><code>
23891 tools:[{
23892     id:'refresh',
23893     qtip: 'Refresh form Data',
23894     // hidden:true,
23895     handler: function(event, toolEl, panel){
23896         // refresh logic
23897     }
23898 },
23899 {
23900     id:'help',
23901     qtip: 'Get Help',
23902     handler: function(event, toolEl, panel){
23903         // whatever
23904     }
23905 }]
23906 </code></pre>
23907      * <p>For the custom id of <code>'help'</code> define two relevant css classes with a link to
23908      * a 15x15 image:</p>
23909      * <pre><code>
23910 .x-tool-help {background-image: url(images/help.png);}
23911 .x-tool-help-over {background-image: url(images/help_over.png);}
23912 // if using an image sprite:
23913 .x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
23914 .x-tool-help-over {background-position:-15px 0;}
23915 </code></pre>
23916      */
23917     /**
23918      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
23919      * <p>A Template used to create {@link #tools} in the {@link #header} Element. Defaults to:</p><pre><code>
23920 new Ext.Template('&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>')</code></pre>
23921      * <p>This may may be overridden to provide a custom DOM structure for tools based upon a more
23922      * complex XTemplate. The template's data is a single tool configuration object (Not the entire Array)
23923      * as specified in {@link #tools}.  In the following example an &lt;a> tag is used to provide a
23924      * visual indication when hovering over the tool:</p><pre><code>
23925 var win = new Ext.Window({
23926     tools: [{
23927         id: 'download',
23928         href: '/MyPdfDoc.pdf'
23929     }],
23930     toolTemplate: new Ext.XTemplate(
23931         '&lt;tpl if="id==\'download\'">',
23932             '&lt;a class="x-tool x-tool-pdf" href="{href}">&lt;/a>',
23933         '&lt;/tpl>',
23934         '&lt;tpl if="id!=\'download\'">',
23935             '&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>',
23936         '&lt;/tpl>'
23937     ),
23938     width:500,
23939     height:300,
23940     closeAction:'hide'
23941 });</code></pre>
23942      * <p>Note that the CSS class 'x-tool-pdf' should have an associated style rule which provides an
23943      * appropriate background image, something like:</p>
23944     <pre><code>
23945     a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
23946     </code></pre>
23947      */
23948     /**
23949      * @cfg {Boolean} hideCollapseTool
23950      * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
23951      * <code>false</code> to display it (defaults to <code>false</code>).
23952      */
23953     /**
23954      * @cfg {Boolean} titleCollapse
23955      * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
23956      * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
23957      * (defaults to <code>false</code>)). If this panel is a child item of a border layout also see the
23958      * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
23959      * <code>{@link Ext.layout.BorderLayout.Region#floatable floatable}</code> config option.
23960      */
23961
23962     /**
23963      * @cfg {Mixed} floating
23964      * <p>This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this
23965      * configuration property are:</p><div class="mdetail-params"><ul>
23966      * <li><b><code>false</code></b> : <b>Default.</b><div class="sub-desc">Display the panel inline where it is
23967      * rendered.</div></li>
23968      * <li><b><code>true</code></b> : <div class="sub-desc">Float the panel (absolute position it with automatic
23969      * shimming and shadow).<ul>
23970      * <div class="sub-desc">Setting floating to true will create an Ext.Layer for this panel and display the
23971      * panel at negative offsets so that it is hidden.</div>
23972      * <div class="sub-desc">Since the panel will be absolute positioned, the position must be set explicitly
23973      * <i>after</i> render (e.g., <code>myPanel.setPosition(100,100);</code>).</div>
23974      * <div class="sub-desc"><b>Note</b>: when floating a panel you should always assign a fixed width,
23975      * otherwise it will be auto width and will expand to fill to the right edge of the viewport.</div>
23976      * </ul></div></li>
23977      * <li><b><code>{@link Ext.Layer object}</code></b> : <div class="sub-desc">The specified object will be used
23978      * as the configuration object for the {@link Ext.Layer} that will be created.</div></li>
23979      * </ul></div>
23980      */
23981     /**
23982      * @cfg {Boolean/String} shadow
23983      * <code>true</code> (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the
23984      * panel, <code>false</code> to display no shadow (defaults to <code>'sides'</code>).  Note that this option
23985      * only applies when <code>{@link #floating} = true</code>.
23986      */
23987     /**
23988      * @cfg {Number} shadowOffset
23989      * The number of pixels to offset the shadow if displayed (defaults to <code>4</code>). Note that this
23990      * option only applies when <code>{@link #floating} = true</code>.
23991      */
23992     /**
23993      * @cfg {Boolean} shim
23994      * <code>false</code> to disable the iframe shim in browsers which need one (defaults to <code>true</code>).
23995      * Note that this option only applies when <code>{@link #floating} = true</code>.
23996      */
23997     /**
23998      * @cfg {Object/Array} keys
23999      * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding}
24000      * used to assign custom key handling to this panel (defaults to <code>null</code>).
24001      */
24002     /**
24003      * @cfg {Boolean/Object} draggable
24004      * <p><code>true</code> to enable dragging of this Panel (defaults to <code>false</code>).</p>
24005      * <p>For custom drag/drop implementations, an <b>Ext.Panel.DD</b> config could also be passed
24006      * in this config instead of <code>true</code>. Ext.Panel.DD is an internal, undocumented class which
24007      * moves a proxy Element around in place of the Panel's element, but provides no other behaviour
24008      * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be
24009      * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.:
24010      * <pre><code>
24011 new Ext.Panel({
24012     title: 'Drag me',
24013     x: 100,
24014     y: 100,
24015     renderTo: Ext.getBody(),
24016     floating: true,
24017     frame: true,
24018     width: 400,
24019     height: 200,
24020     draggable: {
24021 //      Config option of Ext.Panel.DD class.
24022 //      It&#39;s a floating Panel, so do not show a placeholder proxy in the original position.
24023         insertProxy: false,
24024
24025 //      Called for each mousemove event while dragging the DD object.
24026         onDrag : function(e){
24027 //          Record the x,y position of the drag proxy so that we can
24028 //          position the Panel at end of drag.
24029             var pel = this.proxy.getEl();
24030             this.x = pel.getLeft(true);
24031             this.y = pel.getTop(true);
24032
24033 //          Keep the Shadow aligned if there is one.
24034             var s = this.panel.getEl().shadow;
24035             if (s) {
24036                 s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
24037             }
24038         },
24039
24040 //      Called on the mouseup event.
24041         endDrag : function(e){
24042             this.panel.setPosition(this.x, this.y);
24043         }
24044     }
24045 }).show();
24046 </code></pre>
24047      */
24048     /**
24049      * @cfg {Boolean} disabled
24050      * Render this panel disabled (default is <code>false</code>). An important note when using the disabled
24051      * config on panels is that IE will often fail to initialize the disabled mask element correectly if
24052      * the panel's layout has not yet completed by the time the Panel is disabled during the render process.
24053      * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize
24054      * the disabled state:
24055      * <pre><code>
24056 new Ext.Panel({
24057     ...
24058     listeners: {
24059         'afterlayout': {
24060             fn: function(p){
24061                 p.disable();
24062             },
24063             single: true // important, as many layouts can occur
24064         }
24065     }
24066 });
24067 </code></pre>
24068      */
24069     /**
24070      * @cfg {Boolean} autoHeight
24071      * <code>true</code> to use height:'auto', <code>false</code> to use fixed height (defaults to <code>false</code>).
24072      * <b>Note</b>: Setting <code>autoHeight: true</code> means that the browser will manage the panel's height
24073      * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
24074      * manages dimensions (<code>fit</code>, <code>border</code>, etc.) then setting <code>autoHeight: true</code>
24075      * can cause issues with scrolling and will not generally work as expected since the panel will take
24076      * on the height of its contents rather than the height required by the Ext layout.
24077      */
24078
24079
24080     /**
24081      * @cfg {String} baseCls
24082      * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
24083      * <p>Another option available by default is to specify <code>'x-plain'</code> which strips all styling
24084      * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
24085      * See <code>{@link #unstyled}</code> also.</p>
24086      */
24087     baseCls : 'x-panel',
24088     /**
24089      * @cfg {String} collapsedCls
24090      * A CSS class to add to the panel's element after it has been collapsed (defaults to
24091      * <code>'x-panel-collapsed'</code>).
24092      */
24093     collapsedCls : 'x-panel-collapsed',
24094     /**
24095      * @cfg {Boolean} maskDisabled
24096      * <code>true</code> to mask the panel when it is {@link #disabled}, <code>false</code> to not mask it (defaults
24097      * to <code>true</code>).  Either way, the panel will always tell its contained elements to disable themselves
24098      * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
24099      * disabled.
24100      */
24101     maskDisabled : true,
24102     /**
24103      * @cfg {Boolean} animCollapse
24104      * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
24105      * animation (defaults to <code>true</code> if the {@link Ext.Fx} class is available, otherwise <code>false</code>).
24106      */
24107     animCollapse : Ext.enableFx,
24108     /**
24109      * @cfg {Boolean} headerAsText
24110      * <code>true</code> to display the panel <code>{@link #title}</code> in the <code>{@link #header}</code>,
24111      * <code>false</code> to hide it (defaults to <code>true</code>).
24112      */
24113     headerAsText : true,
24114     /**
24115      * @cfg {String} buttonAlign
24116      * The alignment of any {@link #buttons} added to this panel.  Valid values are <code>'right'</code>,
24117      * <code>'left'</code> and <code>'center'</code> (defaults to <code>'right'</code>).
24118      */
24119     buttonAlign : 'right',
24120     /**
24121      * @cfg {Boolean} collapsed
24122      * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
24123      * <code>false</code>).
24124      */
24125     collapsed : false,
24126     /**
24127      * @cfg {Boolean} collapseFirst
24128      * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
24129      * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
24130      */
24131     collapseFirst : true,
24132     /**
24133      * @cfg {Number} minButtonWidth
24134      * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <code>75</code>)
24135      */
24136     minButtonWidth : 75,
24137     /**
24138      * @cfg {Boolean} unstyled
24139      * Overrides the <code>{@link #baseCls}</code> setting to <code>{@link #baseCls} = 'x-plain'</code> which renders
24140      * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
24141      */
24142     /**
24143      * @cfg {String} elements
24144      * A comma-delimited list of panel elements to initialize when the panel is rendered.  Normally, this list will be
24145      * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
24146      * make sure a structural element is rendered even if not specified at config time (for example, you may want
24147      * to add a button or toolbar dynamically after the panel has been rendered).  Adding those elements to this
24148      * list will allocate the required placeholders in the panel when it is rendered.  Valid values are<div class="mdetail-params"><ul>
24149      * <li><code>header</code></li>
24150      * <li><code>tbar</code> (top bar)</li>
24151      * <li><code>body</code></li>
24152      * <li><code>bbar</code> (bottom bar)</li>
24153      * <li><code>footer</code></li>
24154      * </ul></div>
24155      * Defaults to '<code>body</code>'.
24156      */
24157     elements : 'body',
24158     /**
24159      * @cfg {Boolean} preventBodyReset
24160      * Defaults to <code>false</code>.  When set to <code>true</code>, an extra css class <code>'x-panel-normal'</code>
24161      * will be added to the panel's element, effectively applying css styles suggested by the W3C
24162      * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
24163      * footer, etc.).
24164      */
24165     preventBodyReset : false,
24166
24167     /**
24168      * @cfg {Number/String} padding
24169      * A shortcut for setting a padding style on the body element. The value can either be
24170      * a number to be applied to all sides, or a normal css string describing padding.
24171      * Defaults to <tt>undefined</tt>.
24172      *
24173      */
24174     padding: undefined,
24175
24176     /** @cfg {String} resizeEvent
24177      * The event to listen to for resizing in layouts. Defaults to <tt>'bodyresize'</tt>.
24178      */
24179     resizeEvent: 'bodyresize',
24180
24181     // protected - these could be used to customize the behavior of the window,
24182     // but changing them would not be useful without further mofifications and
24183     // could lead to unexpected or undesirable results.
24184     toolTarget : 'header',
24185     collapseEl : 'bwrap',
24186     slideAnchor : 't',
24187     disabledClass : '',
24188
24189     // private, notify box this class will handle heights
24190     deferHeight : true,
24191     // private
24192     expandDefaults: {
24193         duration : 0.25
24194     },
24195     // private
24196     collapseDefaults : {
24197         duration : 0.25
24198     },
24199
24200     // private
24201     initComponent : function(){
24202         Ext.Panel.superclass.initComponent.call(this);
24203
24204         this.addEvents(
24205             /**
24206              * @event bodyresize
24207              * Fires after the Panel has been resized.
24208              * @param {Ext.Panel} p the Panel which has been resized.
24209              * @param {Number} width The Panel body's new width.
24210              * @param {Number} height The Panel body's new height.
24211              */
24212             'bodyresize',
24213             /**
24214              * @event titlechange
24215              * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
24216              * @param {Ext.Panel} p the Panel which has had its title changed.
24217              * @param {String} The new title.
24218              */
24219             'titlechange',
24220             /**
24221              * @event iconchange
24222              * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
24223              * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
24224              * @param {String} The new icon class.
24225              * @param {String} The old icon class.
24226              */
24227             'iconchange',
24228             /**
24229              * @event collapse
24230              * Fires after the Panel has been collapsed.
24231              * @param {Ext.Panel} p the Panel that has been collapsed.
24232              */
24233             'collapse',
24234             /**
24235              * @event expand
24236              * Fires after the Panel has been expanded.
24237              * @param {Ext.Panel} p The Panel that has been expanded.
24238              */
24239             'expand',
24240             /**
24241              * @event beforecollapse
24242              * Fires before the Panel is collapsed.  A handler can return false to cancel the collapse.
24243              * @param {Ext.Panel} p the Panel being collapsed.
24244              * @param {Boolean} animate True if the collapse is animated, else false.
24245              */
24246             'beforecollapse',
24247             /**
24248              * @event beforeexpand
24249              * Fires before the Panel is expanded.  A handler can return false to cancel the expand.
24250              * @param {Ext.Panel} p The Panel being expanded.
24251              * @param {Boolean} animate True if the expand is animated, else false.
24252              */
24253             'beforeexpand',
24254             /**
24255              * @event beforeclose
24256              * Fires before the Panel is closed.  Note that Panels do not directly support being closed, but some
24257              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.  This event only
24258              * applies to such subclasses.
24259              * A handler can return false to cancel the close.
24260              * @param {Ext.Panel} p The Panel being closed.
24261              */
24262             'beforeclose',
24263             /**
24264              * @event close
24265              * Fires after the Panel is closed.  Note that Panels do not directly support being closed, but some
24266              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
24267              * @param {Ext.Panel} p The Panel that has been closed.
24268              */
24269             'close',
24270             /**
24271              * @event activate
24272              * Fires after the Panel has been visually activated.
24273              * Note that Panels do not directly support being activated, but some Panel subclasses
24274              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
24275              * activate and deactivate events under the control of the TabPanel.
24276              * @param {Ext.Panel} p The Panel that has been activated.
24277              */
24278             'activate',
24279             /**
24280              * @event deactivate
24281              * Fires after the Panel has been visually deactivated.
24282              * Note that Panels do not directly support being deactivated, but some Panel subclasses
24283              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
24284              * activate and deactivate events under the control of the TabPanel.
24285              * @param {Ext.Panel} p The Panel that has been deactivated.
24286              */
24287             'deactivate'
24288         );
24289
24290         if(this.unstyled){
24291             this.baseCls = 'x-plain';
24292         }
24293
24294
24295         this.toolbars = [];
24296         // shortcuts
24297         if(this.tbar){
24298             this.elements += ',tbar';
24299             this.topToolbar = this.createToolbar(this.tbar);
24300             this.tbar = null;
24301
24302         }
24303         if(this.bbar){
24304             this.elements += ',bbar';
24305             this.bottomToolbar = this.createToolbar(this.bbar);
24306             this.bbar = null;
24307         }
24308
24309         if(this.header === true){
24310             this.elements += ',header';
24311             this.header = null;
24312         }else if(this.headerCfg || (this.title && this.header !== false)){
24313             this.elements += ',header';
24314         }
24315
24316         if(this.footerCfg || this.footer === true){
24317             this.elements += ',footer';
24318             this.footer = null;
24319         }
24320
24321         if(this.buttons){
24322             this.fbar = this.buttons;
24323             this.buttons = null;
24324         }
24325         if(this.fbar){
24326             this.createFbar(this.fbar);
24327         }
24328         if(this.autoLoad){
24329             this.on('render', this.doAutoLoad, this, {delay:10});
24330         }
24331     },
24332
24333     // private
24334     createFbar : function(fbar){
24335         var min = this.minButtonWidth;
24336         this.elements += ',footer';
24337         this.fbar = this.createToolbar(fbar, {
24338             buttonAlign: this.buttonAlign,
24339             toolbarCls: 'x-panel-fbar',
24340             enableOverflow: false,
24341             defaults: function(c){
24342                 return {
24343                     minWidth: c.minWidth || min
24344                 };
24345             }
24346         });
24347         // @compat addButton and buttons could possibly be removed
24348         // @target 4.0
24349         /**
24350          * This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
24351          * config property. Read only.
24352          * @type Array
24353          * @property buttons
24354          */
24355         this.fbar.items.each(function(c){
24356             c.minWidth = c.minWidth || this.minButtonWidth;
24357         }, this);
24358         this.buttons = this.fbar.items.items;
24359     },
24360
24361     // private
24362     createToolbar: function(tb, options){
24363         var result;
24364         // Convert array to proper toolbar config
24365         if(Ext.isArray(tb)){
24366             tb = {
24367                 items: tb
24368             };
24369         }
24370         result = tb.events ? Ext.apply(tb, options) : this.createComponent(Ext.apply({}, tb, options), 'toolbar');
24371         this.toolbars.push(result);
24372         return result;
24373     },
24374
24375     // private
24376     createElement : function(name, pnode){
24377         if(this[name]){
24378             pnode.appendChild(this[name].dom);
24379             return;
24380         }
24381
24382         if(name === 'bwrap' || this.elements.indexOf(name) != -1){
24383             if(this[name+'Cfg']){
24384                 this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
24385             }else{
24386                 var el = document.createElement('div');
24387                 el.className = this[name+'Cls'];
24388                 this[name] = Ext.get(pnode.appendChild(el));
24389             }
24390             if(this[name+'CssClass']){
24391                 this[name].addClass(this[name+'CssClass']);
24392             }
24393             if(this[name+'Style']){
24394                 this[name].applyStyles(this[name+'Style']);
24395             }
24396         }
24397     },
24398
24399     // private
24400     onRender : function(ct, position){
24401         Ext.Panel.superclass.onRender.call(this, ct, position);
24402         this.createClasses();
24403
24404         var el = this.el,
24405             d = el.dom,
24406             bw,
24407             ts;
24408
24409
24410         if(this.collapsible && !this.hideCollapseTool){
24411             this.tools = this.tools ? this.tools.slice(0) : [];
24412             this.tools[this.collapseFirst?'unshift':'push']({
24413                 id: 'toggle',
24414                 handler : this.toggleCollapse,
24415                 scope: this
24416             });
24417         }
24418
24419         if(this.tools){
24420             ts = this.tools;
24421             this.elements += (this.header !== false) ? ',header' : '';
24422         }
24423         this.tools = {};
24424
24425         el.addClass(this.baseCls);
24426         if(d.firstChild){ // existing markup
24427             this.header = el.down('.'+this.headerCls);
24428             this.bwrap = el.down('.'+this.bwrapCls);
24429             var cp = this.bwrap ? this.bwrap : el;
24430             this.tbar = cp.down('.'+this.tbarCls);
24431             this.body = cp.down('.'+this.bodyCls);
24432             this.bbar = cp.down('.'+this.bbarCls);
24433             this.footer = cp.down('.'+this.footerCls);
24434             this.fromMarkup = true;
24435         }
24436         if (this.preventBodyReset === true) {
24437             el.addClass('x-panel-reset');
24438         }
24439         if(this.cls){
24440             el.addClass(this.cls);
24441         }
24442
24443         if(this.buttons){
24444             this.elements += ',footer';
24445         }
24446
24447         // This block allows for maximum flexibility and performance when using existing markup
24448
24449         // framing requires special markup
24450         if(this.frame){
24451             el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
24452
24453             this.createElement('header', d.firstChild.firstChild.firstChild);
24454             this.createElement('bwrap', d);
24455
24456             // append the mid and bottom frame to the bwrap
24457             bw = this.bwrap.dom;
24458             var ml = d.childNodes[1], bl = d.childNodes[2];
24459             bw.appendChild(ml);
24460             bw.appendChild(bl);
24461
24462             var mc = bw.firstChild.firstChild.firstChild;
24463             this.createElement('tbar', mc);
24464             this.createElement('body', mc);
24465             this.createElement('bbar', mc);
24466             this.createElement('footer', bw.lastChild.firstChild.firstChild);
24467
24468             if(!this.footer){
24469                 this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
24470             }
24471             /*
24472              * Store a reference to this element so:
24473              * a) We aren't looking it up all the time
24474              * b) The last element is reported incorrectly when using a loadmask
24475              */
24476             this.ft = Ext.get(this.bwrap.dom.lastChild);
24477             this.mc = Ext.get(mc);
24478         }else{
24479             this.createElement('header', d);
24480             this.createElement('bwrap', d);
24481
24482             // append the mid and bottom frame to the bwrap
24483             bw = this.bwrap.dom;
24484             this.createElement('tbar', bw);
24485             this.createElement('body', bw);
24486             this.createElement('bbar', bw);
24487             this.createElement('footer', bw);
24488
24489             if(!this.header){
24490                 this.body.addClass(this.bodyCls + '-noheader');
24491                 if(this.tbar){
24492                     this.tbar.addClass(this.tbarCls + '-noheader');
24493                 }
24494             }
24495         }
24496
24497         if(Ext.isDefined(this.padding)){
24498             this.body.setStyle('padding', this.body.addUnits(this.padding));
24499         }
24500
24501         if(this.border === false){
24502             this.el.addClass(this.baseCls + '-noborder');
24503             this.body.addClass(this.bodyCls + '-noborder');
24504             if(this.header){
24505                 this.header.addClass(this.headerCls + '-noborder');
24506             }
24507             if(this.footer){
24508                 this.footer.addClass(this.footerCls + '-noborder');
24509             }
24510             if(this.tbar){
24511                 this.tbar.addClass(this.tbarCls + '-noborder');
24512             }
24513             if(this.bbar){
24514                 this.bbar.addClass(this.bbarCls + '-noborder');
24515             }
24516         }
24517
24518         if(this.bodyBorder === false){
24519            this.body.addClass(this.bodyCls + '-noborder');
24520         }
24521
24522         this.bwrap.enableDisplayMode('block');
24523
24524         if(this.header){
24525             this.header.unselectable();
24526
24527             // for tools, we need to wrap any existing header markup
24528             if(this.headerAsText){
24529                 this.header.dom.innerHTML =
24530                     '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
24531
24532                 if(this.iconCls){
24533                     this.setIconClass(this.iconCls);
24534                 }
24535             }
24536         }
24537
24538         if(this.floating){
24539             this.makeFloating(this.floating);
24540         }
24541
24542         if(this.collapsible && this.titleCollapse && this.header){
24543             this.mon(this.header, 'click', this.toggleCollapse, this);
24544             this.header.setStyle('cursor', 'pointer');
24545         }
24546         if(ts){
24547             this.addTool.apply(this, ts);
24548         }
24549
24550         // Render Toolbars.
24551         if(this.fbar){
24552             this.footer.addClass('x-panel-btns');
24553             this.fbar.ownerCt = this;
24554             this.fbar.render(this.footer);
24555             this.footer.createChild({cls:'x-clear'});
24556         }
24557         if(this.tbar && this.topToolbar){
24558             this.topToolbar.ownerCt = this;
24559             this.topToolbar.render(this.tbar);
24560         }
24561         if(this.bbar && this.bottomToolbar){
24562             this.bottomToolbar.ownerCt = this;
24563             this.bottomToolbar.render(this.bbar);
24564         }
24565     },
24566
24567     /**
24568      * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
24569      * icon class if one has already been set and fire the {@link #iconchange} event after completion.
24570      * @param {String} cls The new CSS class name
24571      */
24572     setIconClass : function(cls){
24573         var old = this.iconCls;
24574         this.iconCls = cls;
24575         if(this.rendered && this.header){
24576             if(this.frame){
24577                 this.header.addClass('x-panel-icon');
24578                 this.header.replaceClass(old, this.iconCls);
24579             }else{
24580                 var hd = this.header,
24581                     img = hd.child('img.x-panel-inline-icon');
24582                 if(img){
24583                     Ext.fly(img).replaceClass(old, this.iconCls);
24584                 }else{
24585                     var hdspan = hd.child('span.' + this.headerTextCls);
24586                     if (hdspan) {
24587                         Ext.DomHelper.insertBefore(hdspan.dom, {
24588                             tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
24589                         });
24590                     }
24591                  }
24592             }
24593         }
24594         this.fireEvent('iconchange', this, cls, old);
24595     },
24596
24597     // private
24598     makeFloating : function(cfg){
24599         this.floating = true;
24600         this.el = new Ext.Layer(Ext.apply({}, cfg, {
24601             shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
24602             shadowOffset: this.shadowOffset,
24603             constrain:false,
24604             shim: this.shim === false ? false : undefined
24605         }), this.el);
24606     },
24607
24608     /**
24609      * Returns the {@link Ext.Toolbar toolbar} from the top (<code>{@link #tbar}</code>) section of the panel.
24610      * @return {Ext.Toolbar} The toolbar
24611      */
24612     getTopToolbar : function(){
24613         return this.topToolbar;
24614     },
24615
24616     /**
24617      * Returns the {@link Ext.Toolbar toolbar} from the bottom (<code>{@link #bbar}</code>) section of the panel.
24618      * @return {Ext.Toolbar} The toolbar
24619      */
24620     getBottomToolbar : function(){
24621         return this.bottomToolbar;
24622     },
24623     
24624     /**
24625      * Returns the {@link Ext.Toolbar toolbar} from the footer (<code>{@link #fbar}</code>) section of the panel.
24626      * @return {Ext.Toolbar} The toolbar
24627      */
24628     getFooterToolbar : function() {
24629         return this.fbar;
24630     },
24631
24632     /**
24633      * Adds a button to this panel.  Note that this method must be called prior to rendering.  The preferred
24634      * approach is to add buttons via the {@link #buttons} config.
24635      * @param {String/Object} config A valid {@link Ext.Button} config.  A string will become the text for a default
24636      * button config, an object will be treated as a button config object.
24637      * @param {Function} handler The function to be called on button {@link Ext.Button#click}
24638      * @param {Object} scope The scope (<code>this</code> reference) in which the button handler function is executed. Defaults to the Button.
24639      * @return {Ext.Button} The button that was added
24640      */
24641     addButton : function(config, handler, scope){
24642         if(!this.fbar){
24643             this.createFbar([]);
24644         }
24645         if(handler){
24646             if(Ext.isString(config)){
24647                 config = {text: config};
24648             }
24649             config = Ext.apply({
24650                 handler: handler,
24651                 scope: scope
24652             }, config);
24653         }
24654         return this.fbar.add(config);
24655     },
24656
24657     // private
24658     addTool : function(){
24659         if(!this.rendered){
24660             if(!this.tools){
24661                 this.tools = [];
24662             }
24663             Ext.each(arguments, function(arg){
24664                 this.tools.push(arg);
24665             }, this);
24666             return;
24667         }
24668          // nowhere to render tools!
24669         if(!this[this.toolTarget]){
24670             return;
24671         }
24672         if(!this.toolTemplate){
24673             // initialize the global tool template on first use
24674             var tt = new Ext.Template(
24675                  '<div class="x-tool x-tool-{id}">&#160;</div>'
24676             );
24677             tt.disableFormats = true;
24678             tt.compile();
24679             Ext.Panel.prototype.toolTemplate = tt;
24680         }
24681         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
24682             var tc = a[i];
24683             if(!this.tools[tc.id]){
24684                 var overCls = 'x-tool-'+tc.id+'-over';
24685                 var t = this.toolTemplate.insertFirst(this[this.toolTarget], tc, true);
24686                 this.tools[tc.id] = t;
24687                 t.enableDisplayMode('block');
24688                 this.mon(t, 'click',  this.createToolHandler(t, tc, overCls, this));
24689                 if(tc.on){
24690                     this.mon(t, tc.on);
24691                 }
24692                 if(tc.hidden){
24693                     t.hide();
24694                 }
24695                 if(tc.qtip){
24696                     if(Ext.isObject(tc.qtip)){
24697                         Ext.QuickTips.register(Ext.apply({
24698                               target: t.id
24699                         }, tc.qtip));
24700                     } else {
24701                         t.dom.qtip = tc.qtip;
24702                     }
24703                 }
24704                 t.addClassOnOver(overCls);
24705             }
24706         }
24707     },
24708
24709     onLayout : function(shallow, force){
24710         Ext.Panel.superclass.onLayout.apply(this, arguments);
24711         if(this.hasLayout && this.toolbars.length > 0){
24712             Ext.each(this.toolbars, function(tb){
24713                 tb.doLayout(undefined, force);
24714             });
24715             this.syncHeight();
24716         }
24717     },
24718
24719     syncHeight : function(){
24720         var h = this.toolbarHeight,
24721                 bd = this.body,
24722                 lsh = this.lastSize.height,
24723                 sz;
24724
24725         if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){
24726             return;
24727         }
24728
24729
24730         if(h != this.getToolbarHeight()){
24731             h = Math.max(0, lsh - this.getFrameHeight());
24732             bd.setHeight(h);
24733             sz = bd.getSize();
24734             this.toolbarHeight = this.getToolbarHeight();
24735             this.onBodyResize(sz.width, sz.height);
24736         }
24737     },
24738
24739     // private
24740     onShow : function(){
24741         if(this.floating){
24742             return this.el.show();
24743         }
24744         Ext.Panel.superclass.onShow.call(this);
24745     },
24746
24747     // private
24748     onHide : function(){
24749         if(this.floating){
24750             return this.el.hide();
24751         }
24752         Ext.Panel.superclass.onHide.call(this);
24753     },
24754
24755     // private
24756     createToolHandler : function(t, tc, overCls, panel){
24757         return function(e){
24758             t.removeClass(overCls);
24759             if(tc.stopEvent !== false){
24760                 e.stopEvent();
24761             }
24762             if(tc.handler){
24763                 tc.handler.call(tc.scope || t, e, t, panel, tc);
24764             }
24765         };
24766     },
24767
24768     // private
24769     afterRender : function(){
24770         if(this.floating && !this.hidden){
24771             this.el.show();
24772         }
24773         if(this.title){
24774             this.setTitle(this.title);
24775         }
24776         Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
24777         if (this.collapsed) {
24778             this.collapsed = false;
24779             this.collapse(false);
24780         }
24781         this.initEvents();
24782     },
24783
24784     // private
24785     getKeyMap : function(){
24786         if(!this.keyMap){
24787             this.keyMap = new Ext.KeyMap(this.el, this.keys);
24788         }
24789         return this.keyMap;
24790     },
24791
24792     // private
24793     initEvents : function(){
24794         if(this.keys){
24795             this.getKeyMap();
24796         }
24797         if(this.draggable){
24798             this.initDraggable();
24799         }
24800         if(this.toolbars.length > 0){
24801             Ext.each(this.toolbars, function(tb){
24802                 tb.doLayout();
24803                 tb.on({
24804                     scope: this,
24805                     afterlayout: this.syncHeight,
24806                     remove: this.syncHeight
24807                 });
24808             }, this);
24809             this.syncHeight();
24810         }
24811
24812     },
24813
24814     // private
24815     initDraggable : function(){
24816         /**
24817          * <p>If this Panel is configured {@link #draggable}, this property will contain
24818          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
24819          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
24820          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
24821          * @type Ext.dd.DragSource.
24822          * @property dd
24823          */
24824         this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
24825     },
24826
24827     // private
24828     beforeEffect : function(anim){
24829         if(this.floating){
24830             this.el.beforeAction();
24831         }
24832         if(anim !== false){
24833             this.el.addClass('x-panel-animated');
24834         }
24835     },
24836
24837     // private
24838     afterEffect : function(anim){
24839         this.syncShadow();
24840         this.el.removeClass('x-panel-animated');
24841     },
24842
24843     // private - wraps up an animation param with internal callbacks
24844     createEffect : function(a, cb, scope){
24845         var o = {
24846             scope:scope,
24847             block:true
24848         };
24849         if(a === true){
24850             o.callback = cb;
24851             return o;
24852         }else if(!a.callback){
24853             o.callback = cb;
24854         }else { // wrap it up
24855             o.callback = function(){
24856                 cb.call(scope);
24857                 Ext.callback(a.callback, a.scope);
24858             };
24859         }
24860         return Ext.applyIf(o, a);
24861     },
24862
24863     /**
24864      * Collapses the panel body so that it becomes hidden.  Fires the {@link #beforecollapse} event which will
24865      * cancel the collapse action if it returns false.
24866      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24867      * {@link #animCollapse} panel config)
24868      * @return {Ext.Panel} this
24869      */
24870     collapse : function(animate){
24871         if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
24872             return;
24873         }
24874         var doAnim = animate === true || (animate !== false && this.animCollapse);
24875         this.beforeEffect(doAnim);
24876         this.onCollapse(doAnim, animate);
24877         return this;
24878     },
24879
24880     // private
24881     onCollapse : function(doAnim, animArg){
24882         if(doAnim){
24883             this[this.collapseEl].slideOut(this.slideAnchor,
24884                     Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
24885                         this.collapseDefaults));
24886         }else{
24887             this[this.collapseEl].hide(this.hideMode);
24888             this.afterCollapse(false);
24889         }
24890     },
24891
24892     // private
24893     afterCollapse : function(anim){
24894         this.collapsed = true;
24895         this.el.addClass(this.collapsedCls);
24896         if(anim !== false){
24897             this[this.collapseEl].hide(this.hideMode);
24898         }
24899         this.afterEffect(anim);
24900
24901         // Reset lastSize of all sub-components so they KNOW they are in a collapsed container
24902         this.cascade(function(c) {
24903             if (c.lastSize) {
24904                 c.lastSize = { width: 0, height: 0 };
24905             }
24906         });
24907         this.fireEvent('collapse', this);
24908     },
24909
24910     /**
24911      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
24912      * cancel the expand action if it returns false.
24913      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24914      * {@link #animCollapse} panel config)
24915      * @return {Ext.Panel} this
24916      */
24917     expand : function(animate){
24918         if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
24919             return;
24920         }
24921         var doAnim = animate === true || (animate !== false && this.animCollapse);
24922         this.el.removeClass(this.collapsedCls);
24923         this.beforeEffect(doAnim);
24924         this.onExpand(doAnim, animate);
24925         return this;
24926     },
24927
24928     // private
24929     onExpand : function(doAnim, animArg){
24930         if(doAnim){
24931             this[this.collapseEl].slideIn(this.slideAnchor,
24932                     Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
24933                         this.expandDefaults));
24934         }else{
24935             this[this.collapseEl].show(this.hideMode);
24936             this.afterExpand(false);
24937         }
24938     },
24939
24940     // private
24941     afterExpand : function(anim){
24942         this.collapsed = false;
24943         if(anim !== false){
24944             this[this.collapseEl].show(this.hideMode);
24945         }
24946         this.afterEffect(anim);
24947         if (this.deferLayout) {
24948             delete this.deferLayout;
24949             this.doLayout(true);
24950         }
24951         this.fireEvent('expand', this);
24952     },
24953
24954     /**
24955      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
24956      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24957      * {@link #animCollapse} panel config)
24958      * @return {Ext.Panel} this
24959      */
24960     toggleCollapse : function(animate){
24961         this[this.collapsed ? 'expand' : 'collapse'](animate);
24962         return this;
24963     },
24964
24965     // private
24966     onDisable : function(){
24967         if(this.rendered && this.maskDisabled){
24968             this.el.mask();
24969         }
24970         Ext.Panel.superclass.onDisable.call(this);
24971     },
24972
24973     // private
24974     onEnable : function(){
24975         if(this.rendered && this.maskDisabled){
24976             this.el.unmask();
24977         }
24978         Ext.Panel.superclass.onEnable.call(this);
24979     },
24980
24981     // private
24982     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
24983         var w = adjWidth,
24984             h = adjHeight;
24985
24986         if(Ext.isDefined(w) || Ext.isDefined(h)){
24987             if(!this.collapsed){
24988                 // First, set the the Panel's body width.
24989                 // If we have auto-widthed it, get the resulting full offset width so we can size the Toolbars to match
24990                 // The Toolbars must not buffer this resize operation because we need to know their heights.
24991
24992                 if(Ext.isNumber(w)){
24993                     this.body.setWidth(w = this.adjustBodyWidth(w - this.getFrameWidth()));
24994                 } else if (w == 'auto') {
24995                     w = this.body.setWidth('auto').dom.offsetWidth;
24996                 } else {
24997                     w = this.body.dom.offsetWidth;
24998                 }
24999
25000                 if(this.tbar){
25001                     this.tbar.setWidth(w);
25002                     if(this.topToolbar){
25003                         this.topToolbar.setSize(w);
25004                     }
25005                 }
25006                 if(this.bbar){
25007                     this.bbar.setWidth(w);
25008                     if(this.bottomToolbar){
25009                         this.bottomToolbar.setSize(w);
25010                         // The bbar does not move on resize without this.
25011                         if (Ext.isIE) {
25012                             this.bbar.setStyle('position', 'static');
25013                             this.bbar.setStyle('position', '');
25014                         }
25015                     }
25016                 }
25017                 if(this.footer){
25018                     this.footer.setWidth(w);
25019                     if(this.fbar){
25020                         this.fbar.setSize(Ext.isIE ? (w - this.footer.getFrameWidth('lr')) : 'auto');
25021                     }
25022                 }
25023
25024                 // At this point, the Toolbars must be layed out for getFrameHeight to find a result.
25025                 if(Ext.isNumber(h)){
25026                     h = Math.max(0, h - this.getFrameHeight());
25027                     //h = Math.max(0, h - (this.getHeight() - this.body.getHeight()));
25028                     this.body.setHeight(h);
25029                 }else if(h == 'auto'){
25030                     this.body.setHeight(h);
25031                 }
25032
25033                 if(this.disabled && this.el._mask){
25034                     this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
25035                 }
25036             }else{
25037                 // Adds an event to set the correct height afterExpand.  This accounts for the deferHeight flag in panel
25038                 this.queuedBodySize = {width: w, height: h};
25039                 if(!this.queuedExpand && this.allowQueuedExpand !== false){
25040                     this.queuedExpand = true;
25041                     this.on('expand', function(){
25042                         delete this.queuedExpand;
25043                         this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
25044                     }, this, {single:true});
25045                 }
25046             }
25047             this.onBodyResize(w, h);
25048         }
25049         this.syncShadow();
25050         Ext.Panel.superclass.onResize.call(this, adjWidth, adjHeight, rawWidth, rawHeight);
25051
25052     },
25053
25054     // private
25055     onBodyResize: function(w, h){
25056         this.fireEvent('bodyresize', this, w, h);
25057     },
25058
25059     // private
25060     getToolbarHeight: function(){
25061         var h = 0;
25062         if(this.rendered){
25063             Ext.each(this.toolbars, function(tb){
25064                 h += tb.getHeight();
25065             }, this);
25066         }
25067         return h;
25068     },
25069
25070     // deprecate
25071     adjustBodyHeight : function(h){
25072         return h;
25073     },
25074
25075     // private
25076     adjustBodyWidth : function(w){
25077         return w;
25078     },
25079
25080     // private
25081     onPosition : function(){
25082         this.syncShadow();
25083     },
25084
25085     /**
25086      * Returns the width in pixels of the framing elements of this panel (not including the body width).  To
25087      * retrieve the body width see {@link #getInnerWidth}.
25088      * @return {Number} The frame width
25089      */
25090     getFrameWidth : function(){
25091         var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr');
25092
25093         if(this.frame){
25094             var l = this.bwrap.dom.firstChild;
25095             w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
25096             w += this.mc.getFrameWidth('lr');
25097         }
25098         return w;
25099     },
25100
25101     /**
25102      * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
25103      * header and footer elements, but not including the body height).  To retrieve the body height see {@link #getInnerHeight}.
25104      * @return {Number} The frame height
25105      */
25106     getFrameHeight : function() {
25107         var h = Math.max(0, this.getHeight() - this.body.getHeight());
25108
25109         if (isNaN(h)) {
25110             h = 0;
25111         }
25112         return h;
25113
25114         /* Deprecate
25115             var h  = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
25116             h += (this.tbar ? this.tbar.getHeight() : 0) +
25117                  (this.bbar ? this.bbar.getHeight() : 0);
25118
25119             if(this.frame){
25120                 h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
25121             }else{
25122                 h += (this.header ? this.header.getHeight() : 0) +
25123                     (this.footer ? this.footer.getHeight() : 0);
25124             }
25125             return h;
25126         */
25127     },
25128
25129     /**
25130      * Returns the width in pixels of the body element (not including the width of any framing elements).
25131      * For the frame width see {@link #getFrameWidth}.
25132      * @return {Number} The body width
25133      */
25134     getInnerWidth : function(){
25135         return this.getSize().width - this.getFrameWidth();
25136     },
25137
25138     /**
25139      * Returns the height in pixels of the body element (not including the height of any framing elements).
25140      * For the frame height see {@link #getFrameHeight}.
25141      * @return {Number} The body height
25142      */
25143     getInnerHeight : function(){
25144         return this.body.getHeight();
25145         /* Deprecate
25146             return this.getSize().height - this.getFrameHeight();
25147         */
25148     },
25149
25150     // private
25151     syncShadow : function(){
25152         if(this.floating){
25153             this.el.sync(true);
25154         }
25155     },
25156
25157     // private
25158     getLayoutTarget : function(){
25159         return this.body;
25160     },
25161
25162     // private
25163     getContentTarget : function(){
25164         return this.body;
25165     },
25166
25167     /**
25168      * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
25169      * <p>In order to be able to set the title, a header element must have been created
25170      * for the Panel. This is triggered either by configuring the Panel with a non-blank <code>{@link #title}</code>,
25171      * or configuring it with <code><b>{@link #header}: true</b></code>.</p>
25172      * @param {String} title The title text to set
25173      * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
25174      */
25175     setTitle : function(title, iconCls){
25176         this.title = title;
25177         if(this.header && this.headerAsText){
25178             this.header.child('span').update(title);
25179         }
25180         if(iconCls){
25181             this.setIconClass(iconCls);
25182         }
25183         this.fireEvent('titlechange', this, title);
25184         return this;
25185     },
25186
25187     /**
25188      * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
25189      * @return {Ext.Updater} The Updater
25190      */
25191     getUpdater : function(){
25192         return this.body.getUpdater();
25193     },
25194
25195      /**
25196      * Loads this content panel immediately with content returned from an XHR call.
25197      * @param {Object/String/Function} config A config object containing any of the following options:
25198 <pre><code>
25199 panel.load({
25200     url: 'your-url.php',
25201     params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
25202     callback: yourFunction,
25203     scope: yourObject, // optional scope for the callback
25204     discardUrl: false,
25205     nocache: false,
25206     text: 'Loading...',
25207     timeout: 30,
25208     scripts: false
25209 });
25210 </code></pre>
25211      * The only required property is url. The optional properties nocache, text and scripts
25212      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
25213      * associated property on this panel Updater instance.
25214      * @return {Ext.Panel} this
25215      */
25216     load : function(){
25217         var um = this.body.getUpdater();
25218         um.update.apply(um, arguments);
25219         return this;
25220     },
25221
25222     // private
25223     beforeDestroy : function(){
25224         Ext.Panel.superclass.beforeDestroy.call(this);
25225         if(this.header){
25226             this.header.removeAllListeners();
25227         }
25228         if(this.tools){
25229             for(var k in this.tools){
25230                 Ext.destroy(this.tools[k]);
25231             }
25232         }
25233         if(this.toolbars.length > 0){
25234             Ext.each(this.toolbars, function(tb){
25235                 tb.un('afterlayout', this.syncHeight, this);
25236                 tb.un('remove', this.syncHeight, this);
25237             }, this);
25238         }
25239         if(Ext.isArray(this.buttons)){
25240             while(this.buttons.length) {
25241                 Ext.destroy(this.buttons[0]);
25242             }
25243         }
25244         if(this.rendered){
25245             Ext.destroy(
25246                 this.ft,
25247                 this.header,
25248                 this.footer,
25249                 this.toolbars,
25250                 this.tbar,
25251                 this.bbar,
25252                 this.body,
25253                 this.mc,
25254                 this.bwrap
25255             );
25256             if (this.fbar) {
25257                 Ext.destroy(
25258                     this.fbar,
25259                     this.fbar.el
25260                 );
25261             }
25262         }else{
25263             Ext.destroy(
25264                 this.topToolbar,
25265                 this.bottomToolbar
25266             );
25267         }
25268     },
25269
25270     // private
25271     createClasses : function(){
25272         this.headerCls = this.baseCls + '-header';
25273         this.headerTextCls = this.baseCls + '-header-text';
25274         this.bwrapCls = this.baseCls + '-bwrap';
25275         this.tbarCls = this.baseCls + '-tbar';
25276         this.bodyCls = this.baseCls + '-body';
25277         this.bbarCls = this.baseCls + '-bbar';
25278         this.footerCls = this.baseCls + '-footer';
25279     },
25280
25281     // private
25282     createGhost : function(cls, useShim, appendTo){
25283         var el = document.createElement('div');
25284         el.className = 'x-panel-ghost ' + (cls ? cls : '');
25285         if(this.header){
25286             el.appendChild(this.el.dom.firstChild.cloneNode(true));
25287         }
25288         Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
25289         el.style.width = this.el.dom.offsetWidth + 'px';;
25290         if(!appendTo){
25291             this.container.dom.appendChild(el);
25292         }else{
25293             Ext.getDom(appendTo).appendChild(el);
25294         }
25295         if(useShim !== false && this.el.useShim !== false){
25296             var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
25297             layer.show();
25298             return layer;
25299         }else{
25300             return new Ext.Element(el);
25301         }
25302     },
25303
25304     // private
25305     doAutoLoad : function(){
25306         var u = this.body.getUpdater();
25307         if(this.renderer){
25308             u.setRenderer(this.renderer);
25309         }
25310         u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
25311     },
25312
25313     /**
25314      * Retrieve a tool by id.
25315      * @param {String} id
25316      * @return {Object} tool
25317      */
25318     getTool : function(id) {
25319         return this.tools[id];
25320     }
25321
25322 /**
25323  * @cfg {String} autoEl @hide
25324  */
25325 });
25326 Ext.reg('panel', Ext.Panel);
25327 /**
25328  * @class Ext.Editor
25329  * @extends Ext.Component
25330  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
25331  * @constructor
25332  * Create a new Editor
25333  * @param {Object} config The config object
25334  * @xtype editor
25335  */
25336 Ext.Editor = function(field, config){
25337     if(field.field){
25338         this.field = Ext.create(field.field, 'textfield');
25339         config = Ext.apply({}, field); // copy so we don't disturb original config
25340         delete config.field;
25341     }else{
25342         this.field = field;
25343     }
25344     Ext.Editor.superclass.constructor.call(this, config);
25345 };
25346
25347 Ext.extend(Ext.Editor, Ext.Component, {
25348     /**
25349     * @cfg {Ext.form.Field} field
25350     * The Field object (or descendant) or config object for field
25351     */
25352     /**
25353      * @cfg {Boolean} allowBlur
25354      * True to {@link #completeEdit complete the editing process} if in edit mode when the
25355      * field is blurred. Defaults to <tt>true</tt>.
25356      */
25357     allowBlur: true,
25358     /**
25359      * @cfg {Boolean/String} autoSize
25360      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
25361      * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
25362      */
25363     /**
25364      * @cfg {Boolean} revertInvalid
25365      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
25366      * validation fails (defaults to true)
25367      */
25368     /**
25369      * @cfg {Boolean} ignoreNoChange
25370      * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
25371      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
25372      * will never be ignored.
25373      */
25374     /**
25375      * @cfg {Boolean} hideEl
25376      * False to keep the bound element visible while the editor is displayed (defaults to true)
25377      */
25378     /**
25379      * @cfg {Mixed} value
25380      * The data value of the underlying field (defaults to "")
25381      */
25382     value : "",
25383     /**
25384      * @cfg {String} alignment
25385      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
25386      */
25387     alignment: "c-c?",
25388     /**
25389      * @cfg {Array} offsets
25390      * The offsets to use when aligning (see {@link Ext.Element#alignTo} for more details. Defaults to <tt>[0, 0]</tt>.
25391      */
25392     offsets: [0, 0],
25393     /**
25394      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
25395      * for bottom-right shadow (defaults to "frame")
25396      */
25397     shadow : "frame",
25398     /**
25399      * @cfg {Boolean} constrain True to constrain the editor to the viewport
25400      */
25401     constrain : false,
25402     /**
25403      * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
25404      */
25405     swallowKeys : true,
25406     /**
25407      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
25408      */
25409     completeOnEnter : true,
25410     /**
25411      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
25412      */
25413     cancelOnEsc : true,
25414     /**
25415      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
25416      */
25417     updateEl : false,
25418
25419     initComponent : function(){
25420         Ext.Editor.superclass.initComponent.call(this);
25421         this.addEvents(
25422             /**
25423              * @event beforestartedit
25424              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
25425              * false from the handler of this event.
25426              * @param {Editor} this
25427              * @param {Ext.Element} boundEl The underlying element bound to this editor
25428              * @param {Mixed} value The field value being set
25429              */
25430             "beforestartedit",
25431             /**
25432              * @event startedit
25433              * Fires when this editor is displayed
25434              * @param {Ext.Element} boundEl The underlying element bound to this editor
25435              * @param {Mixed} value The starting field value
25436              */
25437             "startedit",
25438             /**
25439              * @event beforecomplete
25440              * Fires after a change has been made to the field, but before the change is reflected in the underlying
25441              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
25442              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
25443              * event will not fire since no edit actually occurred.
25444              * @param {Editor} this
25445              * @param {Mixed} value The current field value
25446              * @param {Mixed} startValue The original field value
25447              */
25448             "beforecomplete",
25449             /**
25450              * @event complete
25451              * Fires after editing is complete and any changed value has been written to the underlying field.
25452              * @param {Editor} this
25453              * @param {Mixed} value The current field value
25454              * @param {Mixed} startValue The original field value
25455              */
25456             "complete",
25457             /**
25458              * @event canceledit
25459              * Fires after editing has been canceled and the editor's value has been reset.
25460              * @param {Editor} this
25461              * @param {Mixed} value The user-entered field value that was discarded
25462              * @param {Mixed} startValue The original field value that was set back into the editor after cancel
25463              */
25464             "canceledit",
25465             /**
25466              * @event specialkey
25467              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
25468              * {@link Ext.EventObject#getKey} to determine which key was pressed.
25469              * @param {Ext.form.Field} this
25470              * @param {Ext.EventObject} e The event object
25471              */
25472             "specialkey"
25473         );
25474     },
25475
25476     // private
25477     onRender : function(ct, position){
25478         this.el = new Ext.Layer({
25479             shadow: this.shadow,
25480             cls: "x-editor",
25481             parentEl : ct,
25482             shim : this.shim,
25483             shadowOffset: this.shadowOffset || 4,
25484             id: this.id,
25485             constrain: this.constrain
25486         });
25487         if(this.zIndex){
25488             this.el.setZIndex(this.zIndex);
25489         }
25490         this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
25491         if(this.field.msgTarget != 'title'){
25492             this.field.msgTarget = 'qtip';
25493         }
25494         this.field.inEditor = true;
25495         this.mon(this.field, {
25496             scope: this,
25497             blur: this.onBlur,
25498             specialkey: this.onSpecialKey
25499         });
25500         if(this.field.grow){
25501             this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
25502         }
25503         this.field.render(this.el).show();
25504         this.field.getEl().dom.name = '';
25505         if(this.swallowKeys){
25506             this.field.el.swallowEvent([
25507                 'keypress', // *** Opera
25508                 'keydown'   // *** all other browsers
25509             ]);
25510         }
25511     },
25512
25513     // private
25514     onSpecialKey : function(field, e){
25515         var key = e.getKey(),
25516             complete = this.completeOnEnter && key == e.ENTER,
25517             cancel = this.cancelOnEsc && key == e.ESC;
25518         if(complete || cancel){
25519             e.stopEvent();
25520             if(complete){
25521                 this.completeEdit();
25522             }else{
25523                 this.cancelEdit();
25524             }
25525             if(field.triggerBlur){
25526                 field.triggerBlur();
25527             }
25528         }
25529         this.fireEvent('specialkey', field, e);
25530     },
25531
25532     /**
25533      * Starts the editing process and shows the editor.
25534      * @param {Mixed} el The element to edit
25535      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
25536       * to the innerHTML of el.
25537      */
25538     startEdit : function(el, value){
25539         if(this.editing){
25540             this.completeEdit();
25541         }
25542         this.boundEl = Ext.get(el);
25543         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
25544         if(!this.rendered){
25545             this.render(this.parentEl || document.body);
25546         }
25547         if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
25548             this.startValue = v;
25549             this.field.reset();
25550             this.field.setValue(v);
25551             this.realign(true);
25552             this.editing = true;
25553             this.show();
25554         }
25555     },
25556
25557     // private
25558     doAutoSize : function(){
25559         if(this.autoSize){
25560             var sz = this.boundEl.getSize(),
25561                 fs = this.field.getSize();
25562
25563             switch(this.autoSize){
25564                 case "width":
25565                     this.setSize(sz.width, fs.height);
25566                     break;
25567                 case "height":
25568                     this.setSize(fs.width, sz.height);
25569                     break;
25570                 case "none":
25571                     this.setSize(fs.width, fs.height);
25572                     break;
25573                 default:
25574                     this.setSize(sz.width, sz.height);
25575             }
25576         }
25577     },
25578
25579     /**
25580      * Sets the height and width of this editor.
25581      * @param {Number} width The new width
25582      * @param {Number} height The new height
25583      */
25584     setSize : function(w, h){
25585         delete this.field.lastSize;
25586         this.field.setSize(w, h);
25587         if(this.el){
25588             if(Ext.isGecko2 || Ext.isOpera){
25589                 // prevent layer scrollbars
25590                 this.el.setSize(w, h);
25591             }
25592             this.el.sync();
25593         }
25594     },
25595
25596     /**
25597      * Realigns the editor to the bound field based on the current alignment config value.
25598      * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
25599      */
25600     realign : function(autoSize){
25601         if(autoSize === true){
25602             this.doAutoSize();
25603         }
25604         this.el.alignTo(this.boundEl, this.alignment, this.offsets);
25605     },
25606
25607     /**
25608      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
25609      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
25610      */
25611     completeEdit : function(remainVisible){
25612         if(!this.editing){
25613             return;
25614         }
25615         // Assert combo values first
25616         if (this.field.assertValue) {
25617             this.field.assertValue();
25618         }
25619         var v = this.getValue();
25620         if(!this.field.isValid()){
25621             if(this.revertInvalid !== false){
25622                 this.cancelEdit(remainVisible);
25623             }
25624             return;
25625         }
25626         if(String(v) === String(this.startValue) && this.ignoreNoChange){
25627             this.hideEdit(remainVisible);
25628             return;
25629         }
25630         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
25631             v = this.getValue();
25632             if(this.updateEl && this.boundEl){
25633                 this.boundEl.update(v);
25634             }
25635             this.hideEdit(remainVisible);
25636             this.fireEvent("complete", this, v, this.startValue);
25637         }
25638     },
25639
25640     // private
25641     onShow : function(){
25642         this.el.show();
25643         if(this.hideEl !== false){
25644             this.boundEl.hide();
25645         }
25646         this.field.show().focus(false, true);
25647         this.fireEvent("startedit", this.boundEl, this.startValue);
25648     },
25649
25650     /**
25651      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
25652      * reverted to the original starting value.
25653      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
25654      * cancel (defaults to false)
25655      */
25656     cancelEdit : function(remainVisible){
25657         if(this.editing){
25658             var v = this.getValue();
25659             this.setValue(this.startValue);
25660             this.hideEdit(remainVisible);
25661             this.fireEvent("canceledit", this, v, this.startValue);
25662         }
25663     },
25664
25665     // private
25666     hideEdit: function(remainVisible){
25667         if(remainVisible !== true){
25668             this.editing = false;
25669             this.hide();
25670         }
25671     },
25672
25673     // private
25674     onBlur : function(){
25675         // selectSameEditor flag allows the same editor to be started without onBlur firing on itself
25676         if(this.allowBlur === true && this.editing && this.selectSameEditor !== true){
25677             this.completeEdit();
25678         }
25679     },
25680
25681     // private
25682     onHide : function(){
25683         if(this.editing){
25684             this.completeEdit();
25685             return;
25686         }
25687         this.field.blur();
25688         if(this.field.collapse){
25689             this.field.collapse();
25690         }
25691         this.el.hide();
25692         if(this.hideEl !== false){
25693             this.boundEl.show();
25694         }
25695     },
25696
25697     /**
25698      * Sets the data value of the editor
25699      * @param {Mixed} value Any valid value supported by the underlying field
25700      */
25701     setValue : function(v){
25702         this.field.setValue(v);
25703     },
25704
25705     /**
25706      * Gets the data value of the editor
25707      * @return {Mixed} The data value
25708      */
25709     getValue : function(){
25710         return this.field.getValue();
25711     },
25712
25713     beforeDestroy : function(){
25714         Ext.destroyMembers(this, 'field');
25715
25716         delete this.parentEl;
25717         delete this.boundEl;
25718     }
25719 });
25720 Ext.reg('editor', Ext.Editor);
25721 /**
25722  * @class Ext.ColorPalette
25723  * @extends Ext.Component
25724  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25725  * Here's an example of typical usage:
25726  * <pre><code>
25727 var cp = new Ext.ColorPalette({value:'993300'});  // initial selected color
25728 cp.render('my-div');
25729
25730 cp.on('select', function(palette, selColor){
25731     // do something with selColor
25732 });
25733 </code></pre>
25734  * @constructor
25735  * Create a new ColorPalette
25736  * @param {Object} config The config object
25737  * @xtype colorpalette
25738  */
25739 Ext.ColorPalette = Ext.extend(Ext.Component, {
25740         /**
25741          * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
25742          */
25743     /**
25744      * @cfg {String} itemCls
25745      * The CSS class to apply to the containing element (defaults to 'x-color-palette')
25746      */
25747     itemCls : 'x-color-palette',
25748     /**
25749      * @cfg {String} value
25750      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25751      * the hex codes are case-sensitive.
25752      */
25753     value : null,
25754     /**
25755      * @cfg {String} clickEvent
25756      * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu). 
25757      * Defaults to <tt>'click'</tt>.
25758      */
25759     clickEvent :'click',
25760     // private
25761     ctype : 'Ext.ColorPalette',
25762
25763     /**
25764      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
25765      */
25766     allowReselect : false,
25767
25768     /**
25769      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25770      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25771      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25772      * of colors with the width setting until the box is symmetrical.</p>
25773      * <p>You can override individual colors if needed:</p>
25774      * <pre><code>
25775 var cp = new Ext.ColorPalette();
25776 cp.colors[0] = 'FF0000';  // change the first box to red
25777 </code></pre>
25778
25779 Or you can provide a custom array of your own for complete control:
25780 <pre><code>
25781 var cp = new Ext.ColorPalette();
25782 cp.colors = ['000000', '993300', '333300'];
25783 </code></pre>
25784      * @type Array
25785      */
25786     colors : [
25787         '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
25788         '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
25789         'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
25790         'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
25791         'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
25792     ],
25793
25794     /**
25795      * @cfg {Function} handler
25796      * Optional. A function that will handle the select event of this palette.
25797      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
25798      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
25799      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
25800      * </ul></div>
25801      */
25802     /**
25803      * @cfg {Object} scope
25804      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
25805      * function will be called.  Defaults to this ColorPalette instance.
25806      */
25807     
25808     // private
25809     initComponent : function(){
25810         Ext.ColorPalette.superclass.initComponent.call(this);
25811         this.addEvents(
25812             /**
25813              * @event select
25814              * Fires when a color is selected
25815              * @param {ColorPalette} this
25816              * @param {String} color The 6-digit color hex code (without the # symbol)
25817              */
25818             'select'
25819         );
25820
25821         if(this.handler){
25822             this.on('select', this.handler, this.scope, true);
25823         }    
25824     },
25825
25826     // private
25827     onRender : function(container, position){
25828         this.autoEl = {
25829             tag: 'div',
25830             cls: this.itemCls
25831         };
25832         Ext.ColorPalette.superclass.onRender.call(this, container, position);
25833         var t = this.tpl || new Ext.XTemplate(
25834             '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>'
25835         );
25836         t.overwrite(this.el, this.colors);
25837         this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
25838         if(this.clickEvent != 'click'){
25839                 this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
25840         }
25841     },
25842
25843     // private
25844     afterRender : function(){
25845         Ext.ColorPalette.superclass.afterRender.call(this);
25846         if(this.value){
25847             var s = this.value;
25848             this.value = null;
25849             this.select(s);
25850         }
25851     },
25852
25853     // private
25854     handleClick : function(e, t){
25855         e.preventDefault();
25856         if(!this.disabled){
25857             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25858             this.select(c.toUpperCase());
25859         }
25860     },
25861
25862     /**
25863      * Selects the specified color in the palette (fires the {@link #select} event)
25864      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25865      */
25866     select : function(color){
25867         color = color.replace('#', '');
25868         if(color != this.value || this.allowReselect){
25869             var el = this.el;
25870             if(this.value){
25871                 el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
25872             }
25873             el.child('a.color-'+color).addClass('x-color-palette-sel');
25874             this.value = color;
25875             this.fireEvent('select', this, color);
25876         }
25877     }
25878
25879     /**
25880      * @cfg {String} autoEl @hide
25881      */
25882 });
25883 Ext.reg('colorpalette', Ext.ColorPalette);
25884 /**
25885  * @class Ext.DatePicker
25886  * @extends Ext.Component
25887  * <p>A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
25888  * to allow browsing and selection of valid dates.</p>
25889  * <p>All the string values documented below may be overridden by including an Ext locale file in
25890  * your page.</p>
25891  * @constructor
25892  * Create a new DatePicker
25893  * @param {Object} config The config object
25894  * @xtype datepicker
25895  */
25896 Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
25897     /**
25898      * @cfg {String} todayText
25899      * The text to display on the button that selects the current date (defaults to <code>'Today'</code>)
25900      */
25901     todayText : 'Today',
25902     /**
25903      * @cfg {String} okText
25904      * The text to display on the ok button (defaults to <code>'&#160;OK&#160;'</code> to give the user extra clicking room)
25905      */
25906     okText : '&#160;OK&#160;',
25907     /**
25908      * @cfg {String} cancelText
25909      * The text to display on the cancel button (defaults to <code>'Cancel'</code>)
25910      */
25911     cancelText : 'Cancel',
25912     /**
25913      * @cfg {Function} handler
25914      * Optional. A function that will handle the select event of this picker.
25915      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
25916      * <li><code>picker</code> : DatePicker<div class="sub-desc">This DatePicker.</div></li>
25917      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
25918      * </ul></div>
25919      */
25920     /**
25921      * @cfg {Object} scope
25922      * The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
25923      * function will be called.  Defaults to this DatePicker instance.
25924      */
25925     /**
25926      * @cfg {String} todayTip
25927      * A string used to format the message for displaying in a tooltip over the button that
25928      * selects the current date. Defaults to <code>'{0} (Spacebar)'</code> where
25929      * the <code>{0}</code> token is replaced by today's date.
25930      */
25931     todayTip : '{0} (Spacebar)',
25932     /**
25933      * @cfg {String} minText
25934      * The error text to display if the minDate validation fails (defaults to <code>'This date is before the minimum date'</code>)
25935      */
25936     minText : 'This date is before the minimum date',
25937     /**
25938      * @cfg {String} maxText
25939      * The error text to display if the maxDate validation fails (defaults to <code>'This date is after the maximum date'</code>)
25940      */
25941     maxText : 'This date is after the maximum date',
25942     /**
25943      * @cfg {String} format
25944      * The default date format string which can be overriden for localization support.  The format must be
25945      * valid according to {@link Date#parseDate} (defaults to <code>'m/d/y'</code>).
25946      */
25947     format : 'm/d/y',
25948     /**
25949      * @cfg {String} disabledDaysText
25950      * The tooltip to display when the date falls on a disabled day (defaults to <code>'Disabled'</code>)
25951      */
25952     disabledDaysText : 'Disabled',
25953     /**
25954      * @cfg {String} disabledDatesText
25955      * The tooltip text to display when the date falls on a disabled date (defaults to <code>'Disabled'</code>)
25956      */
25957     disabledDatesText : 'Disabled',
25958     /**
25959      * @cfg {Array} monthNames
25960      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25961      */
25962     monthNames : Date.monthNames,
25963     /**
25964      * @cfg {Array} dayNames
25965      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25966      */
25967     dayNames : Date.dayNames,
25968     /**
25969      * @cfg {String} nextText
25970      * The next month navigation button tooltip (defaults to <code>'Next Month (Control+Right)'</code>)
25971      */
25972     nextText : 'Next Month (Control+Right)',
25973     /**
25974      * @cfg {String} prevText
25975      * The previous month navigation button tooltip (defaults to <code>'Previous Month (Control+Left)'</code>)
25976      */
25977     prevText : 'Previous Month (Control+Left)',
25978     /**
25979      * @cfg {String} monthYearText
25980      * The header month selector tooltip (defaults to <code>'Choose a month (Control+Up/Down to move years)'</code>)
25981      */
25982     monthYearText : 'Choose a month (Control+Up/Down to move years)',
25983     /**
25984      * @cfg {Number} startDay
25985      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25986      */
25987     startDay : 0,
25988     /**
25989      * @cfg {Boolean} showToday
25990      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
25991      * that selects the current date (defaults to <code>true</code>).
25992      */
25993     showToday : true,
25994     /**
25995      * @cfg {Date} minDate
25996      * Minimum allowable date (JavaScript date object, defaults to null)
25997      */
25998     /**
25999      * @cfg {Date} maxDate
26000      * Maximum allowable date (JavaScript date object, defaults to null)
26001      */
26002     /**
26003      * @cfg {Array} disabledDays
26004      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26005      */
26006     /**
26007      * @cfg {RegExp} disabledDatesRE
26008      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}
26009      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
26010      * disabledDates value.
26011      */
26012     /**
26013      * @cfg {Array} disabledDates
26014      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
26015      * expression so they are very powerful. Some examples:
26016      * <ul>
26017      * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>
26018      * <li>['03/08', '09/16'] would disable those days for every year</li>
26019      * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>
26020      * <li>['03/../2006'] would disable every day in March 2006</li>
26021      * <li>['^03'] would disable every day in every March</li>
26022      * </ul>
26023      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
26024      * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
26025      * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
26026      */
26027
26028     // private
26029     // Set by other components to stop the picker focus being updated when the value changes.
26030     focusOnSelect: true,
26031
26032     // default value used to initialise each date in the DatePicker
26033     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
26034     initHour: 12, // 24-hour format
26035
26036     // private
26037     initComponent : function(){
26038         Ext.DatePicker.superclass.initComponent.call(this);
26039
26040         this.value = this.value ?
26041                  this.value.clearTime(true) : new Date().clearTime();
26042
26043         this.addEvents(
26044             /**
26045              * @event select
26046              * Fires when a date is selected
26047              * @param {DatePicker} this DatePicker
26048              * @param {Date} date The selected date
26049              */
26050             'select'
26051         );
26052
26053         if(this.handler){
26054             this.on('select', this.handler,  this.scope || this);
26055         }
26056
26057         this.initDisabledDays();
26058     },
26059
26060     // private
26061     initDisabledDays : function(){
26062         if(!this.disabledDatesRE && this.disabledDates){
26063             var dd = this.disabledDates,
26064                 len = dd.length - 1,
26065                 re = '(?:';
26066
26067             Ext.each(dd, function(d, i){
26068                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
26069                 if(i != len){
26070                     re += '|';
26071                 }
26072             }, this);
26073             this.disabledDatesRE = new RegExp(re + ')');
26074         }
26075     },
26076
26077     /**
26078      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
26079      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
26080      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
26081      */
26082     setDisabledDates : function(dd){
26083         if(Ext.isArray(dd)){
26084             this.disabledDates = dd;
26085             this.disabledDatesRE = null;
26086         }else{
26087             this.disabledDatesRE = dd;
26088         }
26089         this.initDisabledDays();
26090         this.update(this.value, true);
26091     },
26092
26093     /**
26094      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
26095      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
26096      * for details on supported values.
26097      */
26098     setDisabledDays : function(dd){
26099         this.disabledDays = dd;
26100         this.update(this.value, true);
26101     },
26102
26103     /**
26104      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
26105      * @param {Date} value The minimum date that can be selected
26106      */
26107     setMinDate : function(dt){
26108         this.minDate = dt;
26109         this.update(this.value, true);
26110     },
26111
26112     /**
26113      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
26114      * @param {Date} value The maximum date that can be selected
26115      */
26116     setMaxDate : function(dt){
26117         this.maxDate = dt;
26118         this.update(this.value, true);
26119     },
26120
26121     /**
26122      * Sets the value of the date field
26123      * @param {Date} value The date to set
26124      */
26125     setValue : function(value){
26126         this.value = value.clearTime(true);
26127         this.update(this.value);
26128     },
26129
26130     /**
26131      * Gets the current selected value of the date field
26132      * @return {Date} The selected date
26133      */
26134     getValue : function(){
26135         return this.value;
26136     },
26137
26138     // private
26139     focus : function(){
26140         this.update(this.activeDate);
26141     },
26142
26143     // private
26144     onEnable: function(initial){
26145         Ext.DatePicker.superclass.onEnable.call(this);
26146         this.doDisabled(false);
26147         this.update(initial ? this.value : this.activeDate);
26148         if(Ext.isIE){
26149             this.el.repaint();
26150         }
26151
26152     },
26153
26154     // private
26155     onDisable : function(){
26156         Ext.DatePicker.superclass.onDisable.call(this);
26157         this.doDisabled(true);
26158         if(Ext.isIE && !Ext.isIE8){
26159             /* Really strange problem in IE6/7, when disabled, have to explicitly
26160              * repaint each of the nodes to get them to display correctly, simply
26161              * calling repaint on the main element doesn't appear to be enough.
26162              */
26163              Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){
26164                  Ext.fly(el).repaint();
26165              });
26166         }
26167     },
26168
26169     // private
26170     doDisabled : function(disabled){
26171         this.keyNav.setDisabled(disabled);
26172         this.prevRepeater.setDisabled(disabled);
26173         this.nextRepeater.setDisabled(disabled);
26174         if(this.showToday){
26175             this.todayKeyListener.setDisabled(disabled);
26176             this.todayBtn.setDisabled(disabled);
26177         }
26178     },
26179
26180     // private
26181     onRender : function(container, position){
26182         var m = [
26183              '<table cellspacing="0">',
26184                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26185                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],
26186                 dn = this.dayNames,
26187                 i;
26188         for(i = 0; i < 7; i++){
26189             var d = this.startDay+i;
26190             if(d > 6){
26191                 d = d-7;
26192             }
26193             m.push('<th><span>', dn[d].substr(0,1), '</span></th>');
26194         }
26195         m[m.length] = '</tr></thead><tbody><tr>';
26196         for(i = 0; i < 42; i++) {
26197             if(i % 7 === 0 && i !== 0){
26198                 m[m.length] = '</tr><tr>';
26199             }
26200             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26201         }
26202         m.push('</tr></tbody></table></td></tr>',
26203                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',
26204                 '</table><div class="x-date-mp"></div>');
26205
26206         var el = document.createElement('div');
26207         el.className = 'x-date-picker';
26208         el.innerHTML = m.join('');
26209
26210         container.dom.insertBefore(el, position);
26211
26212         this.el = Ext.get(el);
26213         this.eventEl = Ext.get(el.firstChild);
26214
26215         this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
26216             handler: this.showPrevMonth,
26217             scope: this,
26218             preventDefault:true,
26219             stopDefault:true
26220         });
26221
26222         this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
26223             handler: this.showNextMonth,
26224             scope: this,
26225             preventDefault:true,
26226             stopDefault:true
26227         });
26228
26229         this.monthPicker = this.el.down('div.x-date-mp');
26230         this.monthPicker.enableDisplayMode('block');
26231
26232         this.keyNav = new Ext.KeyNav(this.eventEl, {
26233             'left' : function(e){
26234                 if(e.ctrlKey){
26235                     this.showPrevMonth();
26236                 }else{
26237                     this.update(this.activeDate.add('d', -1));
26238                 }
26239             },
26240
26241             'right' : function(e){
26242                 if(e.ctrlKey){
26243                     this.showNextMonth();
26244                 }else{
26245                     this.update(this.activeDate.add('d', 1));
26246                 }
26247             },
26248
26249             'up' : function(e){
26250                 if(e.ctrlKey){
26251                     this.showNextYear();
26252                 }else{
26253                     this.update(this.activeDate.add('d', -7));
26254                 }
26255             },
26256
26257             'down' : function(e){
26258                 if(e.ctrlKey){
26259                     this.showPrevYear();
26260                 }else{
26261                     this.update(this.activeDate.add('d', 7));
26262                 }
26263             },
26264
26265             'pageUp' : function(e){
26266                 this.showNextMonth();
26267             },
26268
26269             'pageDown' : function(e){
26270                 this.showPrevMonth();
26271             },
26272
26273             'enter' : function(e){
26274                 e.stopPropagation();
26275                 return true;
26276             },
26277
26278             scope : this
26279         });
26280
26281         this.el.unselectable();
26282
26283         this.cells = this.el.select('table.x-date-inner tbody td');
26284         this.textNodes = this.el.query('table.x-date-inner tbody span');
26285
26286         this.mbtn = new Ext.Button({
26287             text: '&#160;',
26288             tooltip: this.monthYearText,
26289             renderTo: this.el.child('td.x-date-middle', true)
26290         });
26291         this.mbtn.el.child('em').addClass('x-btn-arrow');
26292
26293         if(this.showToday){
26294             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
26295             var today = (new Date()).dateFormat(this.format);
26296             this.todayBtn = new Ext.Button({
26297                 renderTo: this.el.child('td.x-date-bottom', true),
26298                 text: String.format(this.todayText, today),
26299                 tooltip: String.format(this.todayTip, today),
26300                 handler: this.selectToday,
26301                 scope: this
26302             });
26303         }
26304         this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
26305         this.mon(this.eventEl, 'click', this.handleDateClick,  this, {delegate: 'a.x-date-date'});
26306         this.mon(this.mbtn, 'click', this.showMonthPicker, this);
26307         this.onEnable(true);
26308     },
26309
26310     // private
26311     createMonthPicker : function(){
26312         if(!this.monthPicker.dom.firstChild){
26313             var buf = ['<table border="0" cellspacing="0">'];
26314             for(var i = 0; i < 6; i++){
26315                 buf.push(
26316                     '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',
26317                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',
26318                     i === 0 ?
26319                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26320                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26321                 );
26322             }
26323             buf.push(
26324                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26325                     this.okText,
26326                     '</button><button type="button" class="x-date-mp-cancel">',
26327                     this.cancelText,
26328                     '</button></td></tr>',
26329                 '</table>'
26330             );
26331             this.monthPicker.update(buf.join(''));
26332
26333             this.mon(this.monthPicker, 'click', this.onMonthClick, this);
26334             this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);
26335
26336             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26337             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26338
26339             this.mpMonths.each(function(m, a, i){
26340                 i += 1;
26341                 if((i%2) === 0){
26342                     m.dom.xmonth = 5 + Math.round(i * 0.5);
26343                 }else{
26344                     m.dom.xmonth = Math.round((i-1) * 0.5);
26345                 }
26346             });
26347         }
26348     },
26349
26350     // private
26351     showMonthPicker : function(){
26352         if(!this.disabled){
26353             this.createMonthPicker();
26354             var size = this.el.getSize();
26355             this.monthPicker.setSize(size);
26356             this.monthPicker.child('table').setSize(size);
26357
26358             this.mpSelMonth = (this.activeDate || this.value).getMonth();
26359             this.updateMPMonth(this.mpSelMonth);
26360             this.mpSelYear = (this.activeDate || this.value).getFullYear();
26361             this.updateMPYear(this.mpSelYear);
26362
26363             this.monthPicker.slideIn('t', {duration:0.2});
26364         }
26365     },
26366
26367     // private
26368     updateMPYear : function(y){
26369         this.mpyear = y;
26370         var ys = this.mpYears.elements;
26371         for(var i = 1; i <= 10; i++){
26372             var td = ys[i-1], y2;
26373             if((i%2) === 0){
26374                 y2 = y + Math.round(i * 0.5);
26375                 td.firstChild.innerHTML = y2;
26376                 td.xyear = y2;
26377             }else{
26378                 y2 = y - (5-Math.round(i * 0.5));
26379                 td.firstChild.innerHTML = y2;
26380                 td.xyear = y2;
26381             }
26382             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26383         }
26384     },
26385
26386     // private
26387     updateMPMonth : function(sm){
26388         this.mpMonths.each(function(m, a, i){
26389             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26390         });
26391     },
26392
26393     // private
26394     selectMPMonth : function(m){
26395
26396     },
26397
26398     // private
26399     onMonthClick : function(e, t){
26400         e.stopEvent();
26401         var el = new Ext.Element(t), pn;
26402         if(el.is('button.x-date-mp-cancel')){
26403             this.hideMonthPicker();
26404         }
26405         else if(el.is('button.x-date-mp-ok')){
26406             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
26407             if(d.getMonth() != this.mpSelMonth){
26408                 // 'fix' the JS rolling date conversion if needed
26409                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
26410             }
26411             this.update(d);
26412             this.hideMonthPicker();
26413         }
26414         else if((pn = el.up('td.x-date-mp-month', 2))){
26415             this.mpMonths.removeClass('x-date-mp-sel');
26416             pn.addClass('x-date-mp-sel');
26417             this.mpSelMonth = pn.dom.xmonth;
26418         }
26419         else if((pn = el.up('td.x-date-mp-year', 2))){
26420             this.mpYears.removeClass('x-date-mp-sel');
26421             pn.addClass('x-date-mp-sel');
26422             this.mpSelYear = pn.dom.xyear;
26423         }
26424         else if(el.is('a.x-date-mp-prev')){
26425             this.updateMPYear(this.mpyear-10);
26426         }
26427         else if(el.is('a.x-date-mp-next')){
26428             this.updateMPYear(this.mpyear+10);
26429         }
26430     },
26431
26432     // private
26433     onMonthDblClick : function(e, t){
26434         e.stopEvent();
26435         var el = new Ext.Element(t), pn;
26436         if((pn = el.up('td.x-date-mp-month', 2))){
26437             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26438             this.hideMonthPicker();
26439         }
26440         else if((pn = el.up('td.x-date-mp-year', 2))){
26441             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26442             this.hideMonthPicker();
26443         }
26444     },
26445
26446     // private
26447     hideMonthPicker : function(disableAnim){
26448         if(this.monthPicker){
26449             if(disableAnim === true){
26450                 this.monthPicker.hide();
26451             }else{
26452                 this.monthPicker.slideOut('t', {duration:0.2});
26453             }
26454         }
26455     },
26456
26457     // private
26458     showPrevMonth : function(e){
26459         this.update(this.activeDate.add('mo', -1));
26460     },
26461
26462     // private
26463     showNextMonth : function(e){
26464         this.update(this.activeDate.add('mo', 1));
26465     },
26466
26467     // private
26468     showPrevYear : function(){
26469         this.update(this.activeDate.add('y', -1));
26470     },
26471
26472     // private
26473     showNextYear : function(){
26474         this.update(this.activeDate.add('y', 1));
26475     },
26476
26477     // private
26478     handleMouseWheel : function(e){
26479         e.stopEvent();
26480         if(!this.disabled){
26481             var delta = e.getWheelDelta();
26482             if(delta > 0){
26483                 this.showPrevMonth();
26484             } else if(delta < 0){
26485                 this.showNextMonth();
26486             }
26487         }
26488     },
26489
26490     // private
26491     handleDateClick : function(e, t){
26492         e.stopEvent();
26493         if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
26494             this.cancelFocus = this.focusOnSelect === false;
26495             this.setValue(new Date(t.dateValue));
26496             delete this.cancelFocus;
26497             this.fireEvent('select', this, this.value);
26498         }
26499     },
26500
26501     // private
26502     selectToday : function(){
26503         if(this.todayBtn && !this.todayBtn.disabled){
26504             this.setValue(new Date().clearTime());
26505             this.fireEvent('select', this, this.value);
26506         }
26507     },
26508
26509     // private
26510     update : function(date, forceRefresh){
26511         if(this.rendered){
26512             var vd = this.activeDate, vis = this.isVisible();
26513             this.activeDate = date;
26514             if(!forceRefresh && vd && this.el){
26515                 var t = date.getTime();
26516                 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26517                     this.cells.removeClass('x-date-selected');
26518                     this.cells.each(function(c){
26519                        if(c.dom.firstChild.dateValue == t){
26520                            c.addClass('x-date-selected');
26521                            if(vis && !this.cancelFocus){
26522                                Ext.fly(c.dom.firstChild).focus(50);
26523                            }
26524                            return false;
26525                        }
26526                     }, this);
26527                     return;
26528                 }
26529             }
26530             var days = date.getDaysInMonth(),
26531                 firstOfMonth = date.getFirstDateOfMonth(),
26532                 startingPos = firstOfMonth.getDay()-this.startDay;
26533
26534             if(startingPos < 0){
26535                 startingPos += 7;
26536             }
26537             days += startingPos;
26538
26539             var pm = date.add('mo', -1),
26540                 prevStart = pm.getDaysInMonth()-startingPos,
26541                 cells = this.cells.elements,
26542                 textEls = this.textNodes,
26543                 // convert everything to numbers so it's fast
26544                 d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart, this.initHour)),
26545                 today = new Date().clearTime().getTime(),
26546                 sel = date.clearTime(true).getTime(),
26547                 min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
26548                 max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
26549                 ddMatch = this.disabledDatesRE,
26550                 ddText = this.disabledDatesText,
26551                 ddays = this.disabledDays ? this.disabledDays.join('') : false,
26552                 ddaysText = this.disabledDaysText,
26553                 format = this.format;
26554
26555             if(this.showToday){
26556                 var td = new Date().clearTime(),
26557                     disable = (td < min || td > max ||
26558                     (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
26559                     (ddays && ddays.indexOf(td.getDay()) != -1));
26560
26561                 if(!this.disabled){
26562                     this.todayBtn.setDisabled(disable);
26563                     this.todayKeyListener[disable ? 'disable' : 'enable']();
26564                 }
26565             }
26566
26567             var setCellClass = function(cal, cell){
26568                 cell.title = '';
26569                 var t = d.clearTime(true).getTime();
26570                 cell.firstChild.dateValue = t;
26571                 if(t == today){
26572                     cell.className += ' x-date-today';
26573                     cell.title = cal.todayText;
26574                 }
26575                 if(t == sel){
26576                     cell.className += ' x-date-selected';
26577                     if(vis){
26578                         Ext.fly(cell.firstChild).focus(50);
26579                     }
26580                 }
26581                 // disabling
26582                 if(t < min) {
26583                     cell.className = ' x-date-disabled';
26584                     cell.title = cal.minText;
26585                     return;
26586                 }
26587                 if(t > max) {
26588                     cell.className = ' x-date-disabled';
26589                     cell.title = cal.maxText;
26590                     return;
26591                 }
26592                 if(ddays){
26593                     if(ddays.indexOf(d.getDay()) != -1){
26594                         cell.title = ddaysText;
26595                         cell.className = ' x-date-disabled';
26596                     }
26597                 }
26598                 if(ddMatch && format){
26599                     var fvalue = d.dateFormat(format);
26600                     if(ddMatch.test(fvalue)){
26601                         cell.title = ddText.replace('%0', fvalue);
26602                         cell.className = ' x-date-disabled';
26603                     }
26604                 }
26605             };
26606
26607             var i = 0;
26608             for(; i < startingPos; i++) {
26609                 textEls[i].innerHTML = (++prevStart);
26610                 d.setDate(d.getDate()+1);
26611                 cells[i].className = 'x-date-prevday';
26612                 setCellClass(this, cells[i]);
26613             }
26614             for(; i < days; i++){
26615                 var intDay = i - startingPos + 1;
26616                 textEls[i].innerHTML = (intDay);
26617                 d.setDate(d.getDate()+1);
26618                 cells[i].className = 'x-date-active';
26619                 setCellClass(this, cells[i]);
26620             }
26621             var extraDays = 0;
26622             for(; i < 42; i++) {
26623                  textEls[i].innerHTML = (++extraDays);
26624                  d.setDate(d.getDate()+1);
26625                  cells[i].className = 'x-date-nextday';
26626                  setCellClass(this, cells[i]);
26627             }
26628
26629             this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
26630
26631             if(!this.internalRender){
26632                 var main = this.el.dom.firstChild,
26633                     w = main.offsetWidth;
26634                 this.el.setWidth(w + this.el.getBorderWidth('lr'));
26635                 Ext.fly(main).setWidth(w);
26636                 this.internalRender = true;
26637                 // opera does not respect the auto grow header center column
26638                 // then, after it gets a width opera refuses to recalculate
26639                 // without a second pass
26640                 if(Ext.isOpera && !this.secondPass){
26641                     main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
26642                     this.secondPass = true;
26643                     this.update.defer(10, this, [date]);
26644                 }
26645             }
26646         }
26647     },
26648
26649     // private
26650     beforeDestroy : function() {
26651         if(this.rendered){
26652             Ext.destroy(
26653                 this.keyNav,
26654                 this.monthPicker,
26655                 this.eventEl,
26656                 this.mbtn,
26657                 this.nextRepeater,
26658                 this.prevRepeater,
26659                 this.cells.el,
26660                 this.todayBtn
26661             );
26662             delete this.textNodes;
26663             delete this.cells.elements;
26664         }
26665     }
26666
26667     /**
26668      * @cfg {String} autoEl @hide
26669      */
26670 });
26671
26672 Ext.reg('datepicker', Ext.DatePicker);
26673 /**
26674  * @class Ext.LoadMask
26675  * A simple utility class for generically masking elements while loading data.  If the {@link #store}
26676  * config option is specified, the masking will be automatically synchronized with the store's loading
26677  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
26678  * element's Updater load indicator and will be destroyed after the initial load.
26679  * <p>Example usage:</p>
26680  *<pre><code>
26681 // Basic mask:
26682 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
26683 myMask.show();
26684 </code></pre>
26685  * @constructor
26686  * Create a new LoadMask
26687  * @param {Mixed} el The element or DOM node, or its id
26688  * @param {Object} config The config object
26689  */
26690 Ext.LoadMask = function(el, config){
26691     this.el = Ext.get(el);
26692     Ext.apply(this, config);
26693     if(this.store){
26694         this.store.on({
26695             scope: this,
26696             beforeload: this.onBeforeLoad,
26697             load: this.onLoad,
26698             exception: this.onLoad
26699         });
26700         this.removeMask = Ext.value(this.removeMask, false);
26701     }else{
26702         var um = this.el.getUpdater();
26703         um.showLoadIndicator = false; // disable the default indicator
26704         um.on({
26705             scope: this,
26706             beforeupdate: this.onBeforeLoad,
26707             update: this.onLoad,
26708             failure: this.onLoad
26709         });
26710         this.removeMask = Ext.value(this.removeMask, true);
26711     }
26712 };
26713
26714 Ext.LoadMask.prototype = {
26715     /**
26716      * @cfg {Ext.data.Store} store
26717      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
26718      * hidden on either load sucess, or load fail.
26719      */
26720     /**
26721      * @cfg {Boolean} removeMask
26722      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
26723      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
26724      */
26725     /**
26726      * @cfg {String} msg
26727      * The text to display in a centered loading message box (defaults to 'Loading...')
26728      */
26729     msg : 'Loading...',
26730     /**
26731      * @cfg {String} msgCls
26732      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
26733      */
26734     msgCls : 'x-mask-loading',
26735
26736     /**
26737      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
26738      * @type Boolean
26739      */
26740     disabled: false,
26741
26742     /**
26743      * Disables the mask to prevent it from being displayed
26744      */
26745     disable : function(){
26746        this.disabled = true;
26747     },
26748
26749     /**
26750      * Enables the mask so that it can be displayed
26751      */
26752     enable : function(){
26753         this.disabled = false;
26754     },
26755
26756     // private
26757     onLoad : function(){
26758         this.el.unmask(this.removeMask);
26759     },
26760
26761     // private
26762     onBeforeLoad : function(){
26763         if(!this.disabled){
26764             this.el.mask(this.msg, this.msgCls);
26765         }
26766     },
26767
26768     /**
26769      * Show this LoadMask over the configured Element.
26770      */
26771     show: function(){
26772         this.onBeforeLoad();
26773     },
26774
26775     /**
26776      * Hide this LoadMask.
26777      */
26778     hide: function(){
26779         this.onLoad();
26780     },
26781
26782     // private
26783     destroy : function(){
26784         if(this.store){
26785             this.store.un('beforeload', this.onBeforeLoad, this);
26786             this.store.un('load', this.onLoad, this);
26787             this.store.un('exception', this.onLoad, this);
26788         }else{
26789             var um = this.el.getUpdater();
26790             um.un('beforeupdate', this.onBeforeLoad, this);
26791             um.un('update', this.onLoad, this);
26792             um.un('failure', this.onLoad, this);
26793         }
26794     }
26795 };Ext.ns('Ext.slider');
26796
26797 /**
26798  * @class Ext.slider.Thumb
26799  * @extends Object
26800  * Represents a single thumb element on a Slider. This would not usually be created manually and would instead
26801  * be created internally by an {@link Ext.slider.MultiSlider Ext.Slider}.
26802  */
26803 Ext.slider.Thumb = Ext.extend(Object, {
26804
26805     /**
26806      * @constructor
26807      * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
26808      */
26809     constructor: function(config) {
26810         /**
26811          * @property slider
26812          * @type Ext.slider.MultiSlider
26813          * The slider this thumb is contained within
26814          */
26815         Ext.apply(this, config || {}, {
26816             cls: 'x-slider-thumb',
26817
26818             /**
26819              * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings
26820              */
26821             constrain: false
26822         });
26823
26824         Ext.slider.Thumb.superclass.constructor.call(this, config);
26825
26826         if (this.slider.vertical) {
26827             Ext.apply(this, Ext.slider.Thumb.Vertical);
26828         }
26829     },
26830
26831     /**
26832      * Renders the thumb into a slider
26833      */
26834     render: function() {
26835         this.el = this.slider.innerEl.insertFirst({cls: this.cls});
26836
26837         this.initEvents();
26838     },
26839
26840     /**
26841      * Enables the thumb if it is currently disabled
26842      */
26843     enable: function() {
26844         this.disabled = false;
26845         this.el.removeClass(this.slider.disabledClass);
26846     },
26847
26848     /**
26849      * Disables the thumb if it is currently enabled
26850      */
26851     disable: function() {
26852         this.disabled = true;
26853         this.el.addClass(this.slider.disabledClass);
26854     },
26855
26856     /**
26857      * Sets up an Ext.dd.DragTracker for this thumb
26858      */
26859     initEvents: function() {
26860         var el = this.el;
26861
26862         el.addClassOnOver('x-slider-thumb-over');
26863
26864         this.tracker = new Ext.dd.DragTracker({
26865             onBeforeStart: this.onBeforeDragStart.createDelegate(this),
26866             onStart      : this.onDragStart.createDelegate(this),
26867             onDrag       : this.onDrag.createDelegate(this),
26868             onEnd        : this.onDragEnd.createDelegate(this),
26869             tolerance    : 3,
26870             autoStart    : 300
26871         });
26872
26873         this.tracker.initEl(el);
26874     },
26875
26876     /**
26877      * @private
26878      * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled,
26879      * this returns false to disable the DragTracker too.
26880      * @return {Boolean} False if the slider is currently disabled
26881      */
26882     onBeforeDragStart : function(e) {
26883         if (this.disabled) {
26884             return false;
26885         } else {
26886             this.slider.promoteThumb(this);
26887             return true;
26888         }
26889     },
26890
26891     /**
26892      * @private
26893      * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class
26894      * to the thumb and fires the 'dragstart' event
26895      */
26896     onDragStart: function(e){
26897         this.el.addClass('x-slider-thumb-drag');
26898         this.dragging = true;
26899         this.dragStartValue = this.value;
26900
26901         this.slider.fireEvent('dragstart', this.slider, e, this);
26902     },
26903
26904     /**
26905      * @private
26906      * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time
26907      * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag
26908      */
26909     onDrag: function(e) {
26910         var slider   = this.slider,
26911             index    = this.index,
26912             newValue = this.getNewValue();
26913
26914         if (this.constrain) {
26915             var above = slider.thumbs[index + 1],
26916                 below = slider.thumbs[index - 1];
26917
26918             if (below != undefined && newValue <= below.value) newValue = below.value;
26919             if (above != undefined && newValue >= above.value) newValue = above.value;
26920         }
26921
26922         slider.setValue(index, newValue, false);
26923         slider.fireEvent('drag', slider, e, this);
26924     },
26925
26926     getNewValue: function() {
26927         var slider   = this.slider,
26928             pos      = slider.innerEl.translatePoints(this.tracker.getXY());
26929
26930         return Ext.util.Format.round(slider.reverseValue(pos.left), slider.decimalPrecision);
26931     },
26932
26933     /**
26934      * @private
26935      * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and
26936      * fires the 'changecomplete' event with the new value
26937      */
26938     onDragEnd: function(e) {
26939         var slider = this.slider,
26940             value  = this.value;
26941
26942         this.el.removeClass('x-slider-thumb-drag');
26943
26944         this.dragging = false;
26945         slider.fireEvent('dragend', slider, e);
26946
26947         if (this.dragStartValue != value) {
26948             slider.fireEvent('changecomplete', slider, value, this);
26949         }
26950     }
26951 });
26952
26953 /**
26954  * @class Ext.slider.MultiSlider
26955  * @extends Ext.BoxComponent
26956  * Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis clicking and animation. Can be added as an item to any container. Example usage:
26957 <pre>
26958 new Ext.Slider({
26959     renderTo: Ext.getBody(),
26960     width: 200,
26961     value: 50,
26962     increment: 10,
26963     minValue: 0,
26964     maxValue: 100
26965 });
26966 </pre>
26967  * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
26968 <pre>
26969 new Ext.Slider({
26970     renderTo: Ext.getBody(),
26971     width: 200,
26972     values: [25, 50, 75],
26973     minValue: 0,
26974     maxValue: 100,
26975
26976     //this defaults to true, setting to false allows the thumbs to pass each other
26977     {@link #constrainThumbs}: false
26978 });
26979 </pre>
26980  */
26981 Ext.slider.MultiSlider = Ext.extend(Ext.BoxComponent, {
26982     /**
26983      * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.
26984      */
26985     /**
26986      * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
26987      */
26988     vertical: false,
26989     /**
26990      * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
26991      */
26992     minValue: 0,
26993     /**
26994      * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
26995      */
26996     maxValue: 100,
26997     /**
26998      * @cfg {Number/Boolean} decimalPrecision.
26999      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>
27000      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>
27001      */
27002     decimalPrecision: 0,
27003     /**
27004      * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.
27005      */
27006     keyIncrement: 1,
27007     /**
27008      * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
27009      */
27010     increment: 0,
27011
27012     /**
27013      * @private
27014      * @property clickRange
27015      * @type Array
27016      * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom],
27017      * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top'
27018      * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range
27019      */
27020     clickRange: [5,15],
27021
27022     /**
27023      * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
27024      */
27025     clickToChange : true,
27026     /**
27027      * @cfg {Boolean} animate Turn on or off animation. Defaults to true
27028      */
27029     animate: true,
27030
27031     /**
27032      * True while the thumb is in a drag operation
27033      * @type Boolean
27034      */
27035     dragging: false,
27036
27037     /**
27038      * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
27039      */
27040     constrainThumbs: true,
27041
27042     /**
27043      * @private
27044      * @property topThumbZIndex
27045      * @type Number
27046      * The number used internally to set the z index of the top thumb (see promoteThumb for details)
27047      */
27048     topThumbZIndex: 10000,
27049
27050     // private override
27051     initComponent : function(){
27052         if(!Ext.isDefined(this.value)){
27053             this.value = this.minValue;
27054         }
27055
27056         /**
27057          * @property thumbs
27058          * @type Array
27059          * Array containing references to each thumb
27060          */
27061         this.thumbs = [];
27062
27063         Ext.slider.MultiSlider.superclass.initComponent.call(this);
27064
27065         this.keyIncrement = Math.max(this.increment, this.keyIncrement);
27066         this.addEvents(
27067             /**
27068              * @event beforechange
27069              * Fires before the slider value is changed. By returning false from an event handler,
27070              * you can cancel the event and prevent the slider from changing.
27071              * @param {Ext.Slider} slider The slider
27072              * @param {Number} newValue The new value which the slider is being changed to.
27073              * @param {Number} oldValue The old value which the slider was previously.
27074              */
27075             'beforechange',
27076
27077             /**
27078              * @event change
27079              * Fires when the slider value is changed.
27080              * @param {Ext.Slider} slider The slider
27081              * @param {Number} newValue The new value which the slider has been changed to.
27082              * @param {Ext.slider.Thumb} thumb The thumb that was changed
27083              */
27084             'change',
27085
27086             /**
27087              * @event changecomplete
27088              * Fires when the slider value is changed by the user and any drag operations have completed.
27089              * @param {Ext.Slider} slider The slider
27090              * @param {Number} newValue The new value which the slider has been changed to.
27091              * @param {Ext.slider.Thumb} thumb The thumb that was changed
27092              */
27093             'changecomplete',
27094
27095             /**
27096              * @event dragstart
27097              * Fires after a drag operation has started.
27098              * @param {Ext.Slider} slider The slider
27099              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
27100              */
27101             'dragstart',
27102
27103             /**
27104              * @event drag
27105              * Fires continuously during the drag operation while the mouse is moving.
27106              * @param {Ext.Slider} slider The slider
27107              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
27108              */
27109             'drag',
27110
27111             /**
27112              * @event dragend
27113              * Fires after the drag operation has completed.
27114              * @param {Ext.Slider} slider The slider
27115              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
27116              */
27117             'dragend'
27118         );
27119
27120         /**
27121          * @property values
27122          * @type Array
27123          * Array of values to initalize the thumbs with
27124          */
27125         if (this.values == undefined || Ext.isEmpty(this.values)) this.values = [0];
27126
27127         var values = this.values;
27128
27129         for (var i=0; i < values.length; i++) {
27130             this.addThumb(values[i]);
27131         }
27132
27133         if(this.vertical){
27134             Ext.apply(this, Ext.slider.Vertical);
27135         }
27136     },
27137
27138     /**
27139      * Creates a new thumb and adds it to the slider
27140      * @param {Number} value The initial value to set on the thumb. Defaults to 0
27141      */
27142     addThumb: function(value) {
27143         var thumb = new Ext.slider.Thumb({
27144             value    : value,
27145             slider   : this,
27146             index    : this.thumbs.length,
27147             constrain: this.constrainThumbs
27148         });
27149         this.thumbs.push(thumb);
27150
27151         //render the thumb now if needed
27152         if (this.rendered) thumb.render();
27153     },
27154
27155     /**
27156      * @private
27157      * Moves the given thumb above all other by increasing its z-index. This is called when as drag
27158      * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
27159      * required when the thumbs are stacked on top of each other at one of the ends of the slider's
27160      * range, which can result in the user not being able to move any of them.
27161      * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
27162      */
27163     promoteThumb: function(topThumb) {
27164         var thumbs = this.thumbs,
27165             zIndex, thumb;
27166
27167         for (var i = 0, j = thumbs.length; i < j; i++) {
27168             thumb = thumbs[i];
27169
27170             if (thumb == topThumb) {
27171                 zIndex = this.topThumbZIndex;
27172             } else {
27173                 zIndex = '';
27174             }
27175
27176             thumb.el.setStyle('zIndex', zIndex);
27177         }
27178     },
27179
27180     // private override
27181     onRender : function() {
27182         this.autoEl = {
27183             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),
27184             cn : {
27185                 cls: 'x-slider-end',
27186                 cn : {
27187                     cls:'x-slider-inner',
27188                     cn : [{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]
27189                 }
27190             }
27191         };
27192
27193         Ext.slider.MultiSlider.superclass.onRender.apply(this, arguments);
27194
27195         this.endEl   = this.el.first();
27196         this.innerEl = this.endEl.first();
27197         this.focusEl = this.innerEl.child('.x-slider-focus');
27198
27199         //render each thumb
27200         for (var i=0; i < this.thumbs.length; i++) {
27201             this.thumbs[i].render();
27202         }
27203
27204         //calculate the size of half a thumb
27205         var thumb      = this.innerEl.child('.x-slider-thumb');
27206         this.halfThumb = (this.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
27207
27208         this.initEvents();
27209     },
27210
27211     /**
27212      * @private
27213      * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
27214      * Creates a new DragTracker which is used to control what happens when the user drags the thumb around.
27215      */
27216     initEvents : function(){
27217         this.mon(this.el, {
27218             scope    : this,
27219             mousedown: this.onMouseDown,
27220             keydown  : this.onKeyDown
27221         });
27222
27223         this.focusEl.swallowEvent("click", true);
27224     },
27225
27226     /**
27227      * @private
27228      * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
27229      * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
27230      * @param {Ext.EventObject} e The click event
27231      */
27232     onMouseDown : function(e){
27233         if(this.disabled){
27234             return;
27235         }
27236
27237         //see if the click was on any of the thumbs
27238         var thumbClicked = false;
27239         for (var i=0; i < this.thumbs.length; i++) {
27240             thumbClicked = thumbClicked || e.target == this.thumbs[i].el.dom;
27241         }
27242
27243         if (this.clickToChange && !thumbClicked) {
27244             var local = this.innerEl.translatePoints(e.getXY());
27245             this.onClickChange(local);
27246         }
27247         this.focus();
27248     },
27249
27250     /**
27251      * @private
27252      * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Vertical.
27253      * Only changes the value if the click was within this.clickRange.
27254      * @param {Object} local Object containing top and left values for the click event.
27255      */
27256     onClickChange : function(local) {
27257         if (local.top > this.clickRange[0] && local.top < this.clickRange[1]) {
27258             //find the nearest thumb to the click event
27259             var thumb = this.getNearest(local, 'left'),
27260                 index = thumb.index;
27261
27262             this.setValue(index, Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);
27263         }
27264     },
27265
27266     /**
27267      * @private
27268      * Returns the nearest thumb to a click event, along with its distance
27269      * @param {Object} local Object containing top and left values from a click event
27270      * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
27271      * @return {Object} The closest thumb object and its distance from the click event
27272      */
27273     getNearest: function(local, prop) {
27274         var localValue = prop == 'top' ? this.innerEl.getHeight() - local[prop] : local[prop],
27275             clickValue = this.reverseValue(localValue),
27276             nearestDistance = (this.maxValue - this.minValue) + 5, //add a small fudge for the end of the slider 
27277             index = 0,
27278             nearest = null;
27279
27280         for (var i=0; i < this.thumbs.length; i++) {
27281             var thumb = this.thumbs[i],
27282                 value = thumb.value,
27283                 dist  = Math.abs(value - clickValue);
27284
27285             if (Math.abs(dist <= nearestDistance)) {
27286                 nearest = thumb;
27287                 index = i;
27288                 nearestDistance = dist;
27289             }
27290         }
27291         return nearest;
27292     },
27293
27294     /**
27295      * @private
27296      * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
27297      * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
27298      * @param {Ext.EventObject} e The Event object
27299      */
27300     onKeyDown : function(e){
27301         if(this.disabled){e.preventDefault();return;}
27302         var k = e.getKey();
27303         switch(k){
27304             case e.UP:
27305             case e.RIGHT:
27306                 e.stopEvent();
27307                 if(e.ctrlKey){
27308                     this.setValue(this.maxValue, undefined, true);
27309                 }else{
27310                     this.setValue(this.value+this.keyIncrement, undefined, true);
27311                 }
27312             break;
27313             case e.DOWN:
27314             case e.LEFT:
27315                 e.stopEvent();
27316                 if(e.ctrlKey){
27317                     this.setValue(this.minValue, undefined, true);
27318                 }else{
27319                     this.setValue(this.value-this.keyIncrement, undefined, true);
27320                 }
27321             break;
27322             default:
27323                 e.preventDefault();
27324         }
27325     },
27326
27327     /**
27328      * @private
27329      * If using snapping, this takes a desired new value and returns the closest snapped
27330      * value to it
27331      * @param {Number} value The unsnapped value
27332      * @return {Number} The value of the nearest snap target
27333      */
27334     doSnap : function(value){
27335         if (!(this.increment && value)) {
27336             return value;
27337         }
27338         var newValue = value,
27339             inc = this.increment,
27340             m = value % inc;
27341         if (m != 0) {
27342             newValue -= m;
27343             if (m * 2 >= inc) {
27344                 newValue += inc;
27345             } else if (m * 2 < -inc) {
27346                 newValue -= inc;
27347             }
27348         }
27349         return newValue.constrain(this.minValue,  this.maxValue);
27350     },
27351
27352     // private
27353     afterRender : function(){
27354         Ext.slider.MultiSlider.superclass.afterRender.apply(this, arguments);
27355
27356         for (var i=0; i < this.thumbs.length; i++) {
27357             var thumb = this.thumbs[i];
27358
27359             if (thumb.value !== undefined) {
27360                 var v = this.normalizeValue(thumb.value);
27361
27362                 if (v !== thumb.value) {
27363                     // delete this.value;
27364                     this.setValue(i, v, false);
27365                 } else {
27366                     this.moveThumb(i, this.translateValue(v), false);
27367                 }
27368             }
27369         };
27370     },
27371
27372     /**
27373      * @private
27374      * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
27375      * the ratio is 2
27376      * @return {Number} The ratio of pixels to mapped values
27377      */
27378     getRatio : function(){
27379         var w = this.innerEl.getWidth(),
27380             v = this.maxValue - this.minValue;
27381         return v == 0 ? w : (w/v);
27382     },
27383
27384     /**
27385      * @private
27386      * Returns a snapped, constrained value when given a desired value
27387      * @param {Number} value Raw number value
27388      * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
27389      */
27390     normalizeValue : function(v){
27391         v = this.doSnap(v);
27392         v = Ext.util.Format.round(v, this.decimalPrecision);
27393         v = v.constrain(this.minValue, this.maxValue);
27394         return v;
27395     },
27396
27397     /**
27398      * Sets the minimum value for the slider instance. If the current value is less than the
27399      * minimum value, the current value will be changed.
27400      * @param {Number} val The new minimum value
27401      */
27402     setMinValue : function(val){
27403         this.minValue = val;
27404         this.syncThumb();
27405
27406         for (var i=0, j = this.thumbs.length; i < j; i++) {
27407             if (this.thumbs[i].value < val) this.thumbs[i].value = val;
27408         }
27409     },
27410
27411     /**
27412      * Sets the maximum value for the slider instance. If the current value is more than the
27413      * maximum value, the current value will be changed.
27414      * @param {Number} val The new maximum value
27415      */
27416     setMaxValue : function(val){
27417         this.maxValue = val;
27418         this.syncThumb();
27419
27420         for (var i=0; i < this.thumbs.length; i++) {
27421             if (this.thumbs[i].value > val) this.thumbs[i].value = val;
27422         }
27423     },
27424
27425     /**
27426      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
27427      * the minValue and maxValue.
27428      * @param {Number} index Index of the thumb to move
27429      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
27430      * @param {Boolean} animate Turn on or off animation, defaults to true
27431      */
27432     setValue : function(index, v, animate, changeComplete) {
27433         var thumb = this.thumbs[index],
27434             el    = thumb.el;
27435
27436         v = this.normalizeValue(v);
27437
27438         if (v !== thumb.value && this.fireEvent('beforechange', this, v, thumb.value) !== false) {
27439             thumb.value = v;
27440             this.moveThumb(index, this.translateValue(v), animate !== false);
27441             this.fireEvent('change', this, v, thumb);
27442             if(changeComplete){
27443                 this.fireEvent('changecomplete', this, v, thumb);
27444             }
27445         }
27446     },
27447
27448     /**
27449      * @private
27450      */
27451     translateValue : function(v) {
27452         var ratio = this.getRatio();
27453         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
27454     },
27455
27456     /**
27457      * @private
27458      * Given a pixel location along the slider, returns the mapped slider value for that pixel.
27459      * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
27460      * returns 200
27461      * @param {Number} pos The position along the slider to return a mapped value for
27462      * @return {Number} The mapped value for the given position
27463      */
27464     reverseValue : function(pos){
27465         var ratio = this.getRatio();
27466         return (pos + (this.minValue * ratio)) / ratio;
27467     },
27468
27469     /**
27470      * @private
27471      * @param {Number} index Index of the thumb to move
27472      */
27473     moveThumb: function(index, v, animate){
27474         var thumb = this.thumbs[index].el;
27475
27476         if(!animate || this.animate === false){
27477             thumb.setLeft(v);
27478         }else{
27479             thumb.shift({left: v, stopFx: true, duration:.35});
27480         }
27481     },
27482
27483     // private
27484     focus : function(){
27485         this.focusEl.focus(10);
27486     },
27487
27488     // private
27489     onResize : function(w, h){
27490         var thumbs = this.thumbs,
27491             len = thumbs.length,
27492             i = 0;
27493             
27494         /*
27495          * If we happen to be animating during a resize, the position of the thumb will likely be off
27496          * when the animation stops. As such, just stop any animations before syncing the thumbs.
27497          */
27498         for(; i < len; ++i){
27499             thumbs[i].el.stopFx();    
27500         }
27501         this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));
27502         this.syncThumb();
27503         Ext.slider.MultiSlider.superclass.onResize.apply(this, arguments);
27504     },
27505
27506     //private
27507     onDisable: function(){
27508         Ext.slider.MultiSlider.superclass.onDisable.call(this);
27509
27510         for (var i=0; i < this.thumbs.length; i++) {
27511             var thumb = this.thumbs[i],
27512                 el    = thumb.el;
27513
27514             thumb.disable();
27515
27516             if(Ext.isIE){
27517                 //IE breaks when using overflow visible and opacity other than 1.
27518                 //Create a place holder for the thumb and display it.
27519                 var xy = el.getXY();
27520                 el.hide();
27521
27522                 this.innerEl.addClass(this.disabledClass).dom.disabled = true;
27523
27524                 if (!this.thumbHolder) {
27525                     this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});
27526                 }
27527
27528                 this.thumbHolder.show().setXY(xy);
27529             }
27530         }
27531     },
27532
27533     //private
27534     onEnable: function(){
27535         Ext.slider.MultiSlider.superclass.onEnable.call(this);
27536
27537         for (var i=0; i < this.thumbs.length; i++) {
27538             var thumb = this.thumbs[i],
27539                 el    = thumb.el;
27540
27541             thumb.enable();
27542
27543             if (Ext.isIE) {
27544                 this.innerEl.removeClass(this.disabledClass).dom.disabled = false;
27545
27546                 if (this.thumbHolder) this.thumbHolder.hide();
27547
27548                 el.show();
27549                 this.syncThumb();
27550             }
27551         }
27552     },
27553
27554     /**
27555      * Synchronizes the thumb position to the proper proportion of the total component width based
27556      * on the current slider {@link #value}.  This will be called automatically when the Slider
27557      * is resized by a layout, but if it is rendered auto width, this method can be called from
27558      * another resize handler to sync the Slider if necessary.
27559      */
27560     syncThumb : function() {
27561         if (this.rendered) {
27562             for (var i=0; i < this.thumbs.length; i++) {
27563                 this.moveThumb(i, this.translateValue(this.thumbs[i].value));
27564             }
27565         }
27566     },
27567
27568     /**
27569      * Returns the current value of the slider
27570      * @param {Number} index The index of the thumb to return a value for
27571      * @return {Number} The current value of the slider
27572      */
27573     getValue : function(index) {
27574         return this.thumbs[index].value;
27575     },
27576
27577     /**
27578      * Returns an array of values - one for the location of each thumb
27579      * @return {Array} The set of thumb values
27580      */
27581     getValues: function() {
27582         var values = [];
27583
27584         for (var i=0; i < this.thumbs.length; i++) {
27585             values.push(this.thumbs[i].value);
27586         }
27587
27588         return values;
27589     },
27590
27591     // private
27592     beforeDestroy : function(){
27593         Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder');
27594         Ext.slider.MultiSlider.superclass.beforeDestroy.call(this);
27595     }
27596 });
27597
27598 Ext.reg('multislider', Ext.slider.MultiSlider);
27599
27600 /**
27601  * @class Ext.slider.SingleSlider
27602  * @extends Ext.slider.MultiSlider
27603  * Slider which supports vertical or horizontal orientation, keyboard adjustments,
27604  * configurable snapping, axis clicking and animation. Can be added as an item to
27605  * any container. Example usage:
27606 <pre><code>
27607 new Ext.slider.SingleSlider({
27608     renderTo: Ext.getBody(),
27609     width: 200,
27610     value: 50,
27611     increment: 10,
27612     minValue: 0,
27613     maxValue: 100
27614 });
27615 </code></pre>
27616  * The class Ext.slider.SingleSlider is aliased to Ext.Slider for backwards compatibility.
27617  */
27618 Ext.slider.SingleSlider = Ext.extend(Ext.slider.MultiSlider, {
27619     constructor: function(config) {
27620       config = config || {};
27621
27622       Ext.applyIf(config, {
27623           values: [config.value || 0]
27624       });
27625
27626       Ext.slider.SingleSlider.superclass.constructor.call(this, config);
27627     },
27628
27629     /**
27630      * Returns the current value of the slider
27631      * @return {Number} The current value of the slider
27632      */
27633     getValue: function() {
27634         //just returns the value of the first thumb, which should be the only one in a single slider
27635         return Ext.slider.SingleSlider.superclass.getValue.call(this, 0);
27636     },
27637
27638     /**
27639      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
27640      * the minValue and maxValue.
27641      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
27642      * @param {Boolean} animate Turn on or off animation, defaults to true
27643      */
27644     setValue: function(value, animate) {
27645         var args = Ext.toArray(arguments),
27646             len  = args.length;
27647
27648         //this is to maintain backwards compatiblity for sliders with only one thunb. Usually you must pass the thumb
27649         //index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider
27650         //signature without the required index. The index will always be 0 for a single slider
27651         if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
27652             args.unshift(0);
27653         }
27654
27655         return Ext.slider.SingleSlider.superclass.setValue.apply(this, args);
27656     },
27657
27658     /**
27659      * Synchronizes the thumb position to the proper proportion of the total component width based
27660      * on the current slider {@link #value}.  This will be called automatically when the Slider
27661      * is resized by a layout, but if it is rendered auto width, this method can be called from
27662      * another resize handler to sync the Slider if necessary.
27663      */
27664     syncThumb : function() {
27665         return Ext.slider.SingleSlider.superclass.syncThumb.apply(this, [0].concat(arguments));
27666     },
27667     
27668     // private
27669     getNearest : function(){
27670         // Since there's only 1 thumb, it's always the nearest
27671         return this.thumbs[0];    
27672     }
27673 });
27674
27675 //backwards compatibility
27676 Ext.Slider = Ext.slider.SingleSlider;
27677
27678 Ext.reg('slider', Ext.slider.SingleSlider);
27679
27680 // private class to support vertical sliders
27681 Ext.slider.Vertical = {
27682     onResize : function(w, h){
27683         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));
27684         this.syncThumb();
27685     },
27686
27687     getRatio : function(){
27688         var h = this.innerEl.getHeight(),
27689             v = this.maxValue - this.minValue;
27690         return h/v;
27691     },
27692
27693     moveThumb: function(index, v, animate) {
27694         var thumb = this.thumbs[index],
27695             el    = thumb.el;
27696
27697         if (!animate || this.animate === false) {
27698             el.setBottom(v);
27699         } else {
27700             el.shift({bottom: v, stopFx: true, duration:.35});
27701         }
27702     },
27703
27704     onClickChange : function(local) {
27705         if (local.left > this.clickRange[0] && local.left < this.clickRange[1]) {
27706             var thumb = this.getNearest(local, 'top'),
27707                 index = thumb.index,
27708                 value = this.minValue + this.reverseValue(this.innerEl.getHeight() - local.top);
27709
27710             this.setValue(index, Ext.util.Format.round(value, this.decimalPrecision), undefined, true);
27711         }
27712     }
27713 };
27714
27715 //private class to support vertical dragging of thumbs within a slider
27716 Ext.slider.Thumb.Vertical = {
27717     getNewValue: function() {
27718         var slider   = this.slider,
27719             innerEl  = slider.innerEl,
27720             pos      = innerEl.translatePoints(this.tracker.getXY()),
27721             bottom   = innerEl.getHeight() - pos.top;
27722
27723         return slider.minValue + Ext.util.Format.round(bottom / slider.getRatio(), slider.decimalPrecision);
27724     }
27725 };
27726 /**
27727  * @class Ext.ProgressBar
27728  * @extends Ext.BoxComponent
27729  * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>
27730  * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the
27731  * progress bar as needed from your own code.  This method is most appropriate when you want to show progress
27732  * throughout an operation that has predictable points of interest at which you can update the control.</p>
27733  * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it
27734  * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time
27735  * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in
27736  * which you have no need for indicating intermediate progress.</p>
27737  * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)
27738  * @cfg {String} text The progress bar text (defaults to '')
27739  * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress
27740  * bar's internal text element)
27741  * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)
27742  * @xtype progress
27743  */
27744 Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {
27745    /**
27746     * @cfg {String} baseCls
27747     * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')
27748     */
27749     baseCls : 'x-progress',
27750     
27751     /**
27752     * @cfg {Boolean} animate
27753     * True to animate the progress bar during transitions (defaults to false)
27754     */
27755     animate : false,
27756
27757     // private
27758     waitTimer : null,
27759
27760     // private
27761     initComponent : function(){
27762         Ext.ProgressBar.superclass.initComponent.call(this);
27763         this.addEvents(
27764             /**
27765              * @event update
27766              * Fires after each update interval
27767              * @param {Ext.ProgressBar} this
27768              * @param {Number} The current progress value
27769              * @param {String} The current progress text
27770              */
27771             "update"
27772         );
27773     },
27774
27775     // private
27776     onRender : function(ct, position){
27777         var tpl = new Ext.Template(
27778             '<div class="{cls}-wrap">',
27779                 '<div class="{cls}-inner">',
27780                     '<div class="{cls}-bar">',
27781                         '<div class="{cls}-text">',
27782                             '<div>&#160;</div>',
27783                         '</div>',
27784                     '</div>',
27785                     '<div class="{cls}-text {cls}-text-back">',
27786                         '<div>&#160;</div>',
27787                     '</div>',
27788                 '</div>',
27789             '</div>'
27790         );
27791
27792         this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)
27793             : tpl.append(ct, {cls: this.baseCls}, true);
27794                 
27795         if(this.id){
27796             this.el.dom.id = this.id;
27797         }
27798         var inner = this.el.dom.firstChild;
27799         this.progressBar = Ext.get(inner.firstChild);
27800
27801         if(this.textEl){
27802             //use an external text el
27803             this.textEl = Ext.get(this.textEl);
27804             delete this.textTopEl;
27805         }else{
27806             //setup our internal layered text els
27807             this.textTopEl = Ext.get(this.progressBar.dom.firstChild);
27808             var textBackEl = Ext.get(inner.childNodes[1]);
27809             this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');
27810             this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);
27811             this.textEl.setWidth(inner.offsetWidth);
27812         }
27813         this.progressBar.setHeight(inner.offsetHeight);
27814     },
27815     
27816     // private
27817     afterRender : function(){
27818         Ext.ProgressBar.superclass.afterRender.call(this);
27819         if(this.value){
27820             this.updateProgress(this.value, this.text);
27821         }else{
27822             this.updateText(this.text);
27823         }
27824     },
27825
27826     /**
27827      * Updates the progress bar value, and optionally its text.  If the text argument is not specified,
27828      * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even
27829      * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
27830      * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
27831      * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
27832      * @param {String} text (optional) The string to display in the progress text element (defaults to '')
27833      * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
27834      * not specified, the default for the class is used (default to false)
27835      * @return {Ext.ProgressBar} this
27836      */
27837     updateProgress : function(value, text, animate){
27838         this.value = value || 0;
27839         if(text){
27840             this.updateText(text);
27841         }
27842         if(this.rendered && !this.isDestroyed){
27843             var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);
27844             this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
27845             if(this.textTopEl){
27846                 //textTopEl should be the same width as the bar so overflow will clip as the bar moves
27847                 this.textTopEl.removeClass('x-hidden').setWidth(w);
27848             }
27849         }
27850         this.fireEvent('update', this, value, text);
27851         return this;
27852     },
27853
27854     /**
27855      * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress
27856      * bar will automatically reset after a fixed amount of time and optionally call a callback function
27857      * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must
27858      * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with
27859      * the following properties:
27860      * <pre>
27861 Property   Type          Description
27862 ---------- ------------  ----------------------------------------------------------------------
27863 duration   Number        The length of time in milliseconds that the progress bar should
27864                          run before resetting itself (defaults to undefined, in which case it
27865                          will run indefinitely until reset is called)
27866 interval   Number        The length of time in milliseconds between each progress update
27867                          (defaults to 1000 ms)
27868 animate    Boolean       Whether to animate the transition of the progress bar. If this value is
27869                          not specified, the default for the class is used.                                                   
27870 increment  Number        The number of progress update segments to display within the progress
27871                          bar (defaults to 10).  If the bar reaches the end and is still
27872                          updating, it will automatically wrap back to the beginning.
27873 text       String        Optional text to display in the progress bar element (defaults to '').
27874 fn         Function      A callback function to execute after the progress bar finishes auto-
27875                          updating.  The function will be called with no arguments.  This function
27876                          will be ignored if duration is not specified since in that case the
27877                          progress bar can only be stopped programmatically, so any required function
27878                          should be called by the same code after it resets the progress bar.
27879 scope      Object        The scope that is passed to the callback function (only applies when
27880                          duration and fn are both passed).
27881 </pre>
27882          *
27883          * Example usage:
27884          * <pre><code>
27885 var p = new Ext.ProgressBar({
27886    renderTo: 'my-el'
27887 });
27888
27889 //Wait for 5 seconds, then update the status el (progress bar will auto-reset)
27890 p.wait({
27891    interval: 100, //bar will move fast!
27892    duration: 5000,
27893    increment: 15,
27894    text: 'Updating...',
27895    scope: this,
27896    fn: function(){
27897       Ext.fly('status').update('Done!');
27898    }
27899 });
27900
27901 //Or update indefinitely until some async action completes, then reset manually
27902 p.wait();
27903 myAction.on('complete', function(){
27904     p.reset();
27905     Ext.fly('status').update('Done!');
27906 });
27907 </code></pre>
27908      * @param {Object} config (optional) Configuration options
27909      * @return {Ext.ProgressBar} this
27910      */
27911     wait : function(o){
27912         if(!this.waitTimer){
27913             var scope = this;
27914             o = o || {};
27915             this.updateText(o.text);
27916             this.waitTimer = Ext.TaskMgr.start({
27917                 run: function(i){
27918                     var inc = o.increment || 10;
27919                     i -= 1;
27920                     this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
27921                 },
27922                 interval: o.interval || 1000,
27923                 duration: o.duration,
27924                 onStop: function(){
27925                     if(o.fn){
27926                         o.fn.apply(o.scope || this);
27927                     }
27928                     this.reset();
27929                 },
27930                 scope: scope
27931             });
27932         }
27933         return this;
27934     },
27935
27936     /**
27937      * Returns true if the progress bar is currently in a {@link #wait} operation
27938      * @return {Boolean} True if waiting, else false
27939      */
27940     isWaiting : function(){
27941         return this.waitTimer !== null;
27942     },
27943
27944     /**
27945      * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress
27946      * bar itself will display the updated text.
27947      * @param {String} text (optional) The string to display in the progress text element (defaults to '')
27948      * @return {Ext.ProgressBar} this
27949      */
27950     updateText : function(text){
27951         this.text = text || '&#160;';
27952         if(this.rendered){
27953             this.textEl.update(this.text);
27954         }
27955         return this;
27956     },
27957     
27958     /**
27959      * Synchronizes the inner bar width to the proper proportion of the total componet width based
27960      * on the current progress {@link #value}.  This will be called automatically when the ProgressBar
27961      * is resized by a layout, but if it is rendered auto width, this method can be called from
27962      * another resize handler to sync the ProgressBar if necessary.
27963      */
27964     syncProgressBar : function(){
27965         if(this.value){
27966             this.updateProgress(this.value, this.text);
27967         }
27968         return this;
27969     },
27970
27971     /**
27972      * Sets the size of the progress bar.
27973      * @param {Number} width The new width in pixels
27974      * @param {Number} height The new height in pixels
27975      * @return {Ext.ProgressBar} this
27976      */
27977     setSize : function(w, h){
27978         Ext.ProgressBar.superclass.setSize.call(this, w, h);
27979         if(this.textTopEl){
27980             var inner = this.el.dom.firstChild;
27981             this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);
27982         }
27983         this.syncProgressBar();
27984         return this;
27985     },
27986
27987     /**
27988      * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress
27989      * bar will also be hidden (using the {@link #hideMode} property internally).
27990      * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)
27991      * @return {Ext.ProgressBar} this
27992      */
27993     reset : function(hide){
27994         this.updateProgress(0);
27995         if(this.textTopEl){
27996             this.textTopEl.addClass('x-hidden');
27997         }
27998         this.clearTimer();
27999         if(hide === true){
28000             this.hide();
28001         }
28002         return this;
28003     },
28004     
28005     // private
28006     clearTimer : function(){
28007         if(this.waitTimer){
28008             this.waitTimer.onStop = null; //prevent recursion
28009             Ext.TaskMgr.stop(this.waitTimer);
28010             this.waitTimer = null;
28011         }
28012     },
28013     
28014     onDestroy: function(){
28015         this.clearTimer();
28016         if(this.rendered){
28017             if(this.textEl.isComposite){
28018                 this.textEl.clear();
28019             }
28020             Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');
28021         }
28022         Ext.ProgressBar.superclass.onDestroy.call(this);
28023     }
28024 });
28025 Ext.reg('progress', Ext.ProgressBar);/*
28026  * These classes are derivatives of the similarly named classes in the YUI Library.
28027  * The original license:
28028  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
28029  * Code licensed under the BSD License:
28030  * http://developer.yahoo.net/yui/license.txt
28031  */
28032
28033 (function() {
28034
28035 var Event=Ext.EventManager;
28036 var Dom=Ext.lib.Dom;
28037
28038 /**
28039  * @class Ext.dd.DragDrop
28040  * Defines the interface and base operation of items that that can be
28041  * dragged or can be drop targets.  It was designed to be extended, overriding
28042  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
28043  * Up to three html elements can be associated with a DragDrop instance:
28044  * <ul>
28045  * <li>linked element: the element that is passed into the constructor.
28046  * This is the element which defines the boundaries for interaction with
28047  * other DragDrop objects.</li>
28048  * <li>handle element(s): The drag operation only occurs if the element that
28049  * was clicked matches a handle element.  By default this is the linked
28050  * element, but there are times that you will want only a portion of the
28051  * linked element to initiate the drag operation, and the setHandleElId()
28052  * method provides a way to define this.</li>
28053  * <li>drag element: this represents the element that would be moved along
28054  * with the cursor during a drag operation.  By default, this is the linked
28055  * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
28056  * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
28057  * </li>
28058  * </ul>
28059  * This class should not be instantiated until the onload event to ensure that
28060  * the associated elements are available.
28061  * The following would define a DragDrop obj that would interact with any
28062  * other DragDrop obj in the "group1" group:
28063  * <pre>
28064  *  dd = new Ext.dd.DragDrop("div1", "group1");
28065  * </pre>
28066  * Since none of the event handlers have been implemented, nothing would
28067  * actually happen if you were to run the code above.  Normally you would
28068  * override this class or one of the default implementations, but you can
28069  * also override the methods you want on an instance of the class...
28070  * <pre>
28071  *  dd.onDragDrop = function(e, id) {
28072  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
28073  *  }
28074  * </pre>
28075  * @constructor
28076  * @param {String} id of the element that is linked to this instance
28077  * @param {String} sGroup the group of related DragDrop objects
28078  * @param {object} config an object containing configurable attributes
28079  *                Valid properties for DragDrop:
28080  *                    padding, isTarget, maintainOffset, primaryButtonOnly
28081  */
28082 Ext.dd.DragDrop = function(id, sGroup, config) {
28083     if(id) {
28084         this.init(id, sGroup, config);
28085     }
28086 };
28087
28088 Ext.dd.DragDrop.prototype = {
28089
28090     /**
28091      * Set to false to enable a DragDrop object to fire drag events while dragging
28092      * over its own Element. Defaults to true - DragDrop objects do not by default
28093      * fire drag events to themselves.
28094      * @property ignoreSelf
28095      * @type Boolean
28096      */
28097
28098     /**
28099      * The id of the element associated with this object.  This is what we
28100      * refer to as the "linked element" because the size and position of
28101      * this element is used to determine when the drag and drop objects have
28102      * interacted.
28103      * @property id
28104      * @type String
28105      */
28106     id: null,
28107
28108     /**
28109      * Configuration attributes passed into the constructor
28110      * @property config
28111      * @type object
28112      */
28113     config: null,
28114
28115     /**
28116      * The id of the element that will be dragged.  By default this is same
28117      * as the linked element, but could be changed to another element. Ex:
28118      * Ext.dd.DDProxy
28119      * @property dragElId
28120      * @type String
28121      * @private
28122      */
28123     dragElId: null,
28124
28125     /**
28126      * The ID of the element that initiates the drag operation.  By default
28127      * this is the linked element, but could be changed to be a child of this
28128      * element.  This lets us do things like only starting the drag when the
28129      * header element within the linked html element is clicked.
28130      * @property handleElId
28131      * @type String
28132      * @private
28133      */
28134     handleElId: null,
28135
28136     /**
28137      * An object who's property names identify HTML tags to be considered invalid as drag handles.
28138      * A non-null property value identifies the tag as invalid. Defaults to the 
28139      * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
28140 {
28141     A: "A"
28142 }</code></pre>
28143      * @property invalidHandleTypes
28144      * @type Object
28145      */
28146     invalidHandleTypes: null,
28147
28148     /**
28149      * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
28150      * A non-null property value identifies the ID as invalid. For example, to prevent
28151      * dragging from being initiated on element ID "foo", use:<pre><code>
28152 {
28153     foo: true
28154 }</code></pre>
28155      * @property invalidHandleIds
28156      * @type Object
28157      */
28158     invalidHandleIds: null,
28159
28160     /**
28161      * An Array of CSS class names for elements to be considered in valid as drag handles.
28162      * @property invalidHandleClasses
28163      * @type Array
28164      */
28165     invalidHandleClasses: null,
28166
28167     /**
28168      * The linked element's absolute X position at the time the drag was
28169      * started
28170      * @property startPageX
28171      * @type int
28172      * @private
28173      */
28174     startPageX: 0,
28175
28176     /**
28177      * The linked element's absolute X position at the time the drag was
28178      * started
28179      * @property startPageY
28180      * @type int
28181      * @private
28182      */
28183     startPageY: 0,
28184
28185     /**
28186      * The group defines a logical collection of DragDrop objects that are
28187      * related.  Instances only get events when interacting with other
28188      * DragDrop object in the same group.  This lets us define multiple
28189      * groups using a single DragDrop subclass if we want.
28190      * @property groups
28191      * @type object An object in the format {'group1':true, 'group2':true}
28192      */
28193     groups: null,
28194
28195     /**
28196      * Individual drag/drop instances can be locked.  This will prevent
28197      * onmousedown start drag.
28198      * @property locked
28199      * @type boolean
28200      * @private
28201      */
28202     locked: false,
28203
28204     /**
28205      * Lock this instance
28206      * @method lock
28207      */
28208     lock: function() {
28209         this.locked = true;
28210     },
28211
28212     /**
28213      * When set to true, other DD objects in cooperating DDGroups do not receive
28214      * notification events when this DD object is dragged over them. Defaults to false.
28215      * @property moveOnly
28216      * @type boolean
28217      */
28218     moveOnly: false,
28219
28220     /**
28221      * Unlock this instace
28222      * @method unlock
28223      */
28224     unlock: function() {
28225         this.locked = false;
28226     },
28227
28228     /**
28229      * By default, all instances can be a drop target.  This can be disabled by
28230      * setting isTarget to false.
28231      * @property isTarget
28232      * @type boolean
28233      */
28234     isTarget: true,
28235
28236     /**
28237      * The padding configured for this drag and drop object for calculating
28238      * the drop zone intersection with this object.
28239      * @property padding
28240      * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
28241      */
28242     padding: null,
28243
28244     /**
28245      * Cached reference to the linked element
28246      * @property _domRef
28247      * @private
28248      */
28249     _domRef: null,
28250
28251     /**
28252      * Internal typeof flag
28253      * @property __ygDragDrop
28254      * @private
28255      */
28256     __ygDragDrop: true,
28257
28258     /**
28259      * Set to true when horizontal contraints are applied
28260      * @property constrainX
28261      * @type boolean
28262      * @private
28263      */
28264     constrainX: false,
28265
28266     /**
28267      * Set to true when vertical contraints are applied
28268      * @property constrainY
28269      * @type boolean
28270      * @private
28271      */
28272     constrainY: false,
28273
28274     /**
28275      * The left constraint
28276      * @property minX
28277      * @type int
28278      * @private
28279      */
28280     minX: 0,
28281
28282     /**
28283      * The right constraint
28284      * @property maxX
28285      * @type int
28286      * @private
28287      */
28288     maxX: 0,
28289
28290     /**
28291      * The up constraint
28292      * @property minY
28293      * @type int
28294      * @private
28295      */
28296     minY: 0,
28297
28298     /**
28299      * The down constraint
28300      * @property maxY
28301      * @type int
28302      * @private
28303      */
28304     maxY: 0,
28305
28306     /**
28307      * Maintain offsets when we resetconstraints.  Set to true when you want
28308      * the position of the element relative to its parent to stay the same
28309      * when the page changes
28310      *
28311      * @property maintainOffset
28312      * @type boolean
28313      */
28314     maintainOffset: false,
28315
28316     /**
28317      * Array of pixel locations the element will snap to if we specified a
28318      * horizontal graduation/interval.  This array is generated automatically
28319      * when you define a tick interval.
28320      * @property xTicks
28321      * @type int[]
28322      */
28323     xTicks: null,
28324
28325     /**
28326      * Array of pixel locations the element will snap to if we specified a
28327      * vertical graduation/interval.  This array is generated automatically
28328      * when you define a tick interval.
28329      * @property yTicks
28330      * @type int[]
28331      */
28332     yTicks: null,
28333
28334     /**
28335      * By default the drag and drop instance will only respond to the primary
28336      * button click (left button for a right-handed mouse).  Set to true to
28337      * allow drag and drop to start with any mouse click that is propogated
28338      * by the browser
28339      * @property primaryButtonOnly
28340      * @type boolean
28341      */
28342     primaryButtonOnly: true,
28343
28344     /**
28345      * The available property is false until the linked dom element is accessible.
28346      * @property available
28347      * @type boolean
28348      */
28349     available: false,
28350
28351     /**
28352      * By default, drags can only be initiated if the mousedown occurs in the
28353      * region the linked element is.  This is done in part to work around a
28354      * bug in some browsers that mis-report the mousedown if the previous
28355      * mouseup happened outside of the window.  This property is set to true
28356      * if outer handles are defined.
28357      *
28358      * @property hasOuterHandles
28359      * @type boolean
28360      * @default false
28361      */
28362     hasOuterHandles: false,
28363
28364     /**
28365      * Code that executes immediately before the startDrag event
28366      * @method b4StartDrag
28367      * @private
28368      */
28369     b4StartDrag: function(x, y) { },
28370
28371     /**
28372      * Abstract method called after a drag/drop object is clicked
28373      * and the drag or mousedown time thresholds have beeen met.
28374      * @method startDrag
28375      * @param {int} X click location
28376      * @param {int} Y click location
28377      */
28378     startDrag: function(x, y) { /* override this */ },
28379
28380     /**
28381      * Code that executes immediately before the onDrag event
28382      * @method b4Drag
28383      * @private
28384      */
28385     b4Drag: function(e) { },
28386
28387     /**
28388      * Abstract method called during the onMouseMove event while dragging an
28389      * object.
28390      * @method onDrag
28391      * @param {Event} e the mousemove event
28392      */
28393     onDrag: function(e) { /* override this */ },
28394
28395     /**
28396      * Abstract method called when this element fist begins hovering over
28397      * another DragDrop obj
28398      * @method onDragEnter
28399      * @param {Event} e the mousemove event
28400      * @param {String|DragDrop[]} id In POINT mode, the element
28401      * id this is hovering over.  In INTERSECT mode, an array of one or more
28402      * dragdrop items being hovered over.
28403      */
28404     onDragEnter: function(e, id) { /* override this */ },
28405
28406     /**
28407      * Code that executes immediately before the onDragOver event
28408      * @method b4DragOver
28409      * @private
28410      */
28411     b4DragOver: function(e) { },
28412
28413     /**
28414      * Abstract method called when this element is hovering over another
28415      * DragDrop obj
28416      * @method onDragOver
28417      * @param {Event} e the mousemove event
28418      * @param {String|DragDrop[]} id In POINT mode, the element
28419      * id this is hovering over.  In INTERSECT mode, an array of dd items
28420      * being hovered over.
28421      */
28422     onDragOver: function(e, id) { /* override this */ },
28423
28424     /**
28425      * Code that executes immediately before the onDragOut event
28426      * @method b4DragOut
28427      * @private
28428      */
28429     b4DragOut: function(e) { },
28430
28431     /**
28432      * Abstract method called when we are no longer hovering over an element
28433      * @method onDragOut
28434      * @param {Event} e the mousemove event
28435      * @param {String|DragDrop[]} id In POINT mode, the element
28436      * id this was hovering over.  In INTERSECT mode, an array of dd items
28437      * that the mouse is no longer over.
28438      */
28439     onDragOut: function(e, id) { /* override this */ },
28440
28441     /**
28442      * Code that executes immediately before the onDragDrop event
28443      * @method b4DragDrop
28444      * @private
28445      */
28446     b4DragDrop: function(e) { },
28447
28448     /**
28449      * Abstract method called when this item is dropped on another DragDrop
28450      * obj
28451      * @method onDragDrop
28452      * @param {Event} e the mouseup event
28453      * @param {String|DragDrop[]} id In POINT mode, the element
28454      * id this was dropped on.  In INTERSECT mode, an array of dd items this
28455      * was dropped on.
28456      */
28457     onDragDrop: function(e, id) { /* override this */ },
28458
28459     /**
28460      * Abstract method called when this item is dropped on an area with no
28461      * drop target
28462      * @method onInvalidDrop
28463      * @param {Event} e the mouseup event
28464      */
28465     onInvalidDrop: function(e) { /* override this */ },
28466
28467     /**
28468      * Code that executes immediately before the endDrag event
28469      * @method b4EndDrag
28470      * @private
28471      */
28472     b4EndDrag: function(e) { },
28473
28474     /**
28475      * Fired when we are done dragging the object
28476      * @method endDrag
28477      * @param {Event} e the mouseup event
28478      */
28479     endDrag: function(e) { /* override this */ },
28480
28481     /**
28482      * Code executed immediately before the onMouseDown event
28483      * @method b4MouseDown
28484      * @param {Event} e the mousedown event
28485      * @private
28486      */
28487     b4MouseDown: function(e) {  },
28488
28489     /**
28490      * Event handler that fires when a drag/drop obj gets a mousedown
28491      * @method onMouseDown
28492      * @param {Event} e the mousedown event
28493      */
28494     onMouseDown: function(e) { /* override this */ },
28495
28496     /**
28497      * Event handler that fires when a drag/drop obj gets a mouseup
28498      * @method onMouseUp
28499      * @param {Event} e the mouseup event
28500      */
28501     onMouseUp: function(e) { /* override this */ },
28502
28503     /**
28504      * Override the onAvailable method to do what is needed after the initial
28505      * position was determined.
28506      * @method onAvailable
28507      */
28508     onAvailable: function () {
28509     },
28510
28511     /**
28512      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
28513      * @type Object
28514      */
28515     defaultPadding : {left:0, right:0, top:0, bottom:0},
28516
28517     /**
28518      * Initializes the drag drop object's constraints to restrict movement to a certain element.
28519  *
28520  * Usage:
28521  <pre><code>
28522  var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
28523                 { dragElId: "existingProxyDiv" });
28524  dd.startDrag = function(){
28525      this.constrainTo("parent-id");
28526  };
28527  </code></pre>
28528  * Or you can initalize it using the {@link Ext.Element} object:
28529  <pre><code>
28530  Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
28531      startDrag : function(){
28532          this.constrainTo("parent-id");
28533      }
28534  });
28535  </code></pre>
28536      * @param {Mixed} constrainTo The element to constrain to.
28537      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
28538      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
28539      * an object containing the sides to pad. For example: {right:10, bottom:10}
28540      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
28541      */
28542     constrainTo : function(constrainTo, pad, inContent){
28543         if(Ext.isNumber(pad)){
28544             pad = {left: pad, right:pad, top:pad, bottom:pad};
28545         }
28546         pad = pad || this.defaultPadding;
28547         var b = Ext.get(this.getEl()).getBox(),
28548             ce = Ext.get(constrainTo),
28549             s = ce.getScroll(),
28550             c, 
28551             cd = ce.dom;
28552         if(cd == document.body){
28553             c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()};
28554         }else{
28555             var xy = ce.getXY();
28556             c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
28557         }
28558
28559
28560         var topSpace = b.y - c.y,
28561             leftSpace = b.x - c.x;
28562
28563         this.resetConstraints();
28564         this.setXConstraint(leftSpace - (pad.left||0), // left
28565                 c.width - leftSpace - b.width - (pad.right||0), //right
28566                                 this.xTickSize
28567         );
28568         this.setYConstraint(topSpace - (pad.top||0), //top
28569                 c.height - topSpace - b.height - (pad.bottom||0), //bottom
28570                                 this.yTickSize
28571         );
28572     },
28573
28574     /**
28575      * Returns a reference to the linked element
28576      * @method getEl
28577      * @return {HTMLElement} the html element
28578      */
28579     getEl: function() {
28580         if (!this._domRef) {
28581             this._domRef = Ext.getDom(this.id);
28582         }
28583
28584         return this._domRef;
28585     },
28586
28587     /**
28588      * Returns a reference to the actual element to drag.  By default this is
28589      * the same as the html element, but it can be assigned to another
28590      * element. An example of this can be found in Ext.dd.DDProxy
28591      * @method getDragEl
28592      * @return {HTMLElement} the html element
28593      */
28594     getDragEl: function() {
28595         return Ext.getDom(this.dragElId);
28596     },
28597
28598     /**
28599      * Sets up the DragDrop object.  Must be called in the constructor of any
28600      * Ext.dd.DragDrop subclass
28601      * @method init
28602      * @param id the id of the linked element
28603      * @param {String} sGroup the group of related items
28604      * @param {object} config configuration attributes
28605      */
28606     init: function(id, sGroup, config) {
28607         this.initTarget(id, sGroup, config);
28608         Event.on(this.id, "mousedown", this.handleMouseDown, this);
28609         // Event.on(this.id, "selectstart", Event.preventDefault);
28610     },
28611
28612     /**
28613      * Initializes Targeting functionality only... the object does not
28614      * get a mousedown handler.
28615      * @method initTarget
28616      * @param id the id of the linked element
28617      * @param {String} sGroup the group of related items
28618      * @param {object} config configuration attributes
28619      */
28620     initTarget: function(id, sGroup, config) {
28621
28622         // configuration attributes
28623         this.config = config || {};
28624
28625         // create a local reference to the drag and drop manager
28626         this.DDM = Ext.dd.DDM;
28627         // initialize the groups array
28628         this.groups = {};
28629
28630         // assume that we have an element reference instead of an id if the
28631         // parameter is not a string
28632         if (typeof id !== "string") {
28633             id = Ext.id(id);
28634         }
28635
28636         // set the id
28637         this.id = id;
28638
28639         // add to an interaction group
28640         this.addToGroup((sGroup) ? sGroup : "default");
28641
28642         // We don't want to register this as the handle with the manager
28643         // so we just set the id rather than calling the setter.
28644         this.handleElId = id;
28645
28646         // the linked element is the element that gets dragged by default
28647         this.setDragElId(id);
28648
28649         // by default, clicked anchors will not start drag operations.
28650         this.invalidHandleTypes = { A: "A" };
28651         this.invalidHandleIds = {};
28652         this.invalidHandleClasses = [];
28653
28654         this.applyConfig();
28655
28656         this.handleOnAvailable();
28657     },
28658
28659     /**
28660      * Applies the configuration parameters that were passed into the constructor.
28661      * This is supposed to happen at each level through the inheritance chain.  So
28662      * a DDProxy implentation will execute apply config on DDProxy, DD, and
28663      * DragDrop in order to get all of the parameters that are available in
28664      * each object.
28665      * @method applyConfig
28666      */
28667     applyConfig: function() {
28668
28669         // configurable properties:
28670         //    padding, isTarget, maintainOffset, primaryButtonOnly
28671         this.padding           = this.config.padding || [0, 0, 0, 0];
28672         this.isTarget          = (this.config.isTarget !== false);
28673         this.maintainOffset    = (this.config.maintainOffset);
28674         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
28675
28676     },
28677
28678     /**
28679      * Executed when the linked element is available
28680      * @method handleOnAvailable
28681      * @private
28682      */
28683     handleOnAvailable: function() {
28684         this.available = true;
28685         this.resetConstraints();
28686         this.onAvailable();
28687     },
28688
28689      /**
28690      * Configures the padding for the target zone in px.  Effectively expands
28691      * (or reduces) the virtual object size for targeting calculations.
28692      * Supports css-style shorthand; if only one parameter is passed, all sides
28693      * will have that padding, and if only two are passed, the top and bottom
28694      * will have the first param, the left and right the second.
28695      * @method setPadding
28696      * @param {int} iTop    Top pad
28697      * @param {int} iRight  Right pad
28698      * @param {int} iBot    Bot pad
28699      * @param {int} iLeft   Left pad
28700      */
28701     setPadding: function(iTop, iRight, iBot, iLeft) {
28702         // this.padding = [iLeft, iRight, iTop, iBot];
28703         if (!iRight && 0 !== iRight) {
28704             this.padding = [iTop, iTop, iTop, iTop];
28705         } else if (!iBot && 0 !== iBot) {
28706             this.padding = [iTop, iRight, iTop, iRight];
28707         } else {
28708             this.padding = [iTop, iRight, iBot, iLeft];
28709         }
28710     },
28711
28712     /**
28713      * Stores the initial placement of the linked element.
28714      * @method setInitPosition
28715      * @param {int} diffX   the X offset, default 0
28716      * @param {int} diffY   the Y offset, default 0
28717      */
28718     setInitPosition: function(diffX, diffY) {
28719         var el = this.getEl();
28720
28721         if (!this.DDM.verifyEl(el)) {
28722             return;
28723         }
28724
28725         var dx = diffX || 0;
28726         var dy = diffY || 0;
28727
28728         var p = Dom.getXY( el );
28729
28730         this.initPageX = p[0] - dx;
28731         this.initPageY = p[1] - dy;
28732
28733         this.lastPageX = p[0];
28734         this.lastPageY = p[1];
28735
28736         this.setStartPosition(p);
28737     },
28738
28739     /**
28740      * Sets the start position of the element.  This is set when the obj
28741      * is initialized, the reset when a drag is started.
28742      * @method setStartPosition
28743      * @param pos current position (from previous lookup)
28744      * @private
28745      */
28746     setStartPosition: function(pos) {
28747         var p = pos || Dom.getXY( this.getEl() );
28748         this.deltaSetXY = null;
28749
28750         this.startPageX = p[0];
28751         this.startPageY = p[1];
28752     },
28753
28754     /**
28755      * Add this instance to a group of related drag/drop objects.  All
28756      * instances belong to at least one group, and can belong to as many
28757      * groups as needed.
28758      * @method addToGroup
28759      * @param sGroup {string} the name of the group
28760      */
28761     addToGroup: function(sGroup) {
28762         this.groups[sGroup] = true;
28763         this.DDM.regDragDrop(this, sGroup);
28764     },
28765
28766     /**
28767      * Remove's this instance from the supplied interaction group
28768      * @method removeFromGroup
28769      * @param {string}  sGroup  The group to drop
28770      */
28771     removeFromGroup: function(sGroup) {
28772         if (this.groups[sGroup]) {
28773             delete this.groups[sGroup];
28774         }
28775
28776         this.DDM.removeDDFromGroup(this, sGroup);
28777     },
28778
28779     /**
28780      * Allows you to specify that an element other than the linked element
28781      * will be moved with the cursor during a drag
28782      * @method setDragElId
28783      * @param id {string} the id of the element that will be used to initiate the drag
28784      */
28785     setDragElId: function(id) {
28786         this.dragElId = id;
28787     },
28788
28789     /**
28790      * Allows you to specify a child of the linked element that should be
28791      * used to initiate the drag operation.  An example of this would be if
28792      * you have a content div with text and links.  Clicking anywhere in the
28793      * content area would normally start the drag operation.  Use this method
28794      * to specify that an element inside of the content div is the element
28795      * that starts the drag operation.
28796      * @method setHandleElId
28797      * @param id {string} the id of the element that will be used to
28798      * initiate the drag.
28799      */
28800     setHandleElId: function(id) {
28801         if (typeof id !== "string") {
28802             id = Ext.id(id);
28803         }
28804         this.handleElId = id;
28805         this.DDM.regHandle(this.id, id);
28806     },
28807
28808     /**
28809      * Allows you to set an element outside of the linked element as a drag
28810      * handle
28811      * @method setOuterHandleElId
28812      * @param id the id of the element that will be used to initiate the drag
28813      */
28814     setOuterHandleElId: function(id) {
28815         if (typeof id !== "string") {
28816             id = Ext.id(id);
28817         }
28818         Event.on(id, "mousedown",
28819                 this.handleMouseDown, this);
28820         this.setHandleElId(id);
28821
28822         this.hasOuterHandles = true;
28823     },
28824
28825     /**
28826      * Remove all drag and drop hooks for this element
28827      * @method unreg
28828      */
28829     unreg: function() {
28830         Event.un(this.id, "mousedown",
28831                 this.handleMouseDown);
28832         this._domRef = null;
28833         this.DDM._remove(this);
28834     },
28835
28836     destroy : function(){
28837         this.unreg();
28838     },
28839
28840     /**
28841      * Returns true if this instance is locked, or the drag drop mgr is locked
28842      * (meaning that all drag/drop is disabled on the page.)
28843      * @method isLocked
28844      * @return {boolean} true if this obj or all drag/drop is locked, else
28845      * false
28846      */
28847     isLocked: function() {
28848         return (this.DDM.isLocked() || this.locked);
28849     },
28850
28851     /**
28852      * Fired when this object is clicked
28853      * @method handleMouseDown
28854      * @param {Event} e
28855      * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
28856      * @private
28857      */
28858     handleMouseDown: function(e, oDD){
28859         if (this.primaryButtonOnly && e.button != 0) {
28860             return;
28861         }
28862
28863         if (this.isLocked()) {
28864             return;
28865         }
28866
28867         this.DDM.refreshCache(this.groups);
28868
28869         var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e));
28870         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
28871         } else {
28872             if (this.clickValidator(e)) {
28873
28874                 // set the initial element position
28875                 this.setStartPosition();
28876
28877                 this.b4MouseDown(e);
28878                 this.onMouseDown(e);
28879
28880                 this.DDM.handleMouseDown(e, this);
28881
28882                 this.DDM.stopEvent(e);
28883             } else {
28884
28885
28886             }
28887         }
28888     },
28889
28890     clickValidator: function(e) {
28891         var target = e.getTarget();
28892         return ( this.isValidHandleChild(target) &&
28893                     (this.id == this.handleElId ||
28894                         this.DDM.handleWasClicked(target, this.id)) );
28895     },
28896
28897     /**
28898      * Allows you to specify a tag name that should not start a drag operation
28899      * when clicked.  This is designed to facilitate embedding links within a
28900      * drag handle that do something other than start the drag.
28901      * @method addInvalidHandleType
28902      * @param {string} tagName the type of element to exclude
28903      */
28904     addInvalidHandleType: function(tagName) {
28905         var type = tagName.toUpperCase();
28906         this.invalidHandleTypes[type] = type;
28907     },
28908
28909     /**
28910      * Lets you to specify an element id for a child of a drag handle
28911      * that should not initiate a drag
28912      * @method addInvalidHandleId
28913      * @param {string} id the element id of the element you wish to ignore
28914      */
28915     addInvalidHandleId: function(id) {
28916         if (typeof id !== "string") {
28917             id = Ext.id(id);
28918         }
28919         this.invalidHandleIds[id] = id;
28920     },
28921
28922     /**
28923      * Lets you specify a css class of elements that will not initiate a drag
28924      * @method addInvalidHandleClass
28925      * @param {string} cssClass the class of the elements you wish to ignore
28926      */
28927     addInvalidHandleClass: function(cssClass) {
28928         this.invalidHandleClasses.push(cssClass);
28929     },
28930
28931     /**
28932      * Unsets an excluded tag name set by addInvalidHandleType
28933      * @method removeInvalidHandleType
28934      * @param {string} tagName the type of element to unexclude
28935      */
28936     removeInvalidHandleType: function(tagName) {
28937         var type = tagName.toUpperCase();
28938         // this.invalidHandleTypes[type] = null;
28939         delete this.invalidHandleTypes[type];
28940     },
28941
28942     /**
28943      * Unsets an invalid handle id
28944      * @method removeInvalidHandleId
28945      * @param {string} id the id of the element to re-enable
28946      */
28947     removeInvalidHandleId: function(id) {
28948         if (typeof id !== "string") {
28949             id = Ext.id(id);
28950         }
28951         delete this.invalidHandleIds[id];
28952     },
28953
28954     /**
28955      * Unsets an invalid css class
28956      * @method removeInvalidHandleClass
28957      * @param {string} cssClass the class of the element(s) you wish to
28958      * re-enable
28959      */
28960     removeInvalidHandleClass: function(cssClass) {
28961         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
28962             if (this.invalidHandleClasses[i] == cssClass) {
28963                 delete this.invalidHandleClasses[i];
28964             }
28965         }
28966     },
28967
28968     /**
28969      * Checks the tag exclusion list to see if this click should be ignored
28970      * @method isValidHandleChild
28971      * @param {HTMLElement} node the HTMLElement to evaluate
28972      * @return {boolean} true if this is a valid tag type, false if not
28973      */
28974     isValidHandleChild: function(node) {
28975
28976         var valid = true;
28977         // var n = (node.nodeName == "#text") ? node.parentNode : node;
28978         var nodeName;
28979         try {
28980             nodeName = node.nodeName.toUpperCase();
28981         } catch(e) {
28982             nodeName = node.nodeName;
28983         }
28984         valid = valid && !this.invalidHandleTypes[nodeName];
28985         valid = valid && !this.invalidHandleIds[node.id];
28986
28987         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
28988             valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]);
28989         }
28990
28991
28992         return valid;
28993
28994     },
28995
28996     /**
28997      * Create the array of horizontal tick marks if an interval was specified
28998      * in setXConstraint().
28999      * @method setXTicks
29000      * @private
29001      */
29002     setXTicks: function(iStartX, iTickSize) {
29003         this.xTicks = [];
29004         this.xTickSize = iTickSize;
29005
29006         var tickMap = {};
29007
29008         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
29009             if (!tickMap[i]) {
29010                 this.xTicks[this.xTicks.length] = i;
29011                 tickMap[i] = true;
29012             }
29013         }
29014
29015         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
29016             if (!tickMap[i]) {
29017                 this.xTicks[this.xTicks.length] = i;
29018                 tickMap[i] = true;
29019             }
29020         }
29021
29022         this.xTicks.sort(this.DDM.numericSort) ;
29023     },
29024
29025     /**
29026      * Create the array of vertical tick marks if an interval was specified in
29027      * setYConstraint().
29028      * @method setYTicks
29029      * @private
29030      */
29031     setYTicks: function(iStartY, iTickSize) {
29032         this.yTicks = [];
29033         this.yTickSize = iTickSize;
29034
29035         var tickMap = {};
29036
29037         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
29038             if (!tickMap[i]) {
29039                 this.yTicks[this.yTicks.length] = i;
29040                 tickMap[i] = true;
29041             }
29042         }
29043
29044         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
29045             if (!tickMap[i]) {
29046                 this.yTicks[this.yTicks.length] = i;
29047                 tickMap[i] = true;
29048             }
29049         }
29050
29051         this.yTicks.sort(this.DDM.numericSort) ;
29052     },
29053
29054     /**
29055      * By default, the element can be dragged any place on the screen.  Use
29056      * this method to limit the horizontal travel of the element.  Pass in
29057      * 0,0 for the parameters if you want to lock the drag to the y axis.
29058      * @method setXConstraint
29059      * @param {int} iLeft the number of pixels the element can move to the left
29060      * @param {int} iRight the number of pixels the element can move to the
29061      * right
29062      * @param {int} iTickSize optional parameter for specifying that the
29063      * element
29064      * should move iTickSize pixels at a time.
29065      */
29066     setXConstraint: function(iLeft, iRight, iTickSize) {
29067         this.leftConstraint = iLeft;
29068         this.rightConstraint = iRight;
29069
29070         this.minX = this.initPageX - iLeft;
29071         this.maxX = this.initPageX + iRight;
29072         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
29073
29074         this.constrainX = true;
29075     },
29076
29077     /**
29078      * Clears any constraints applied to this instance.  Also clears ticks
29079      * since they can't exist independent of a constraint at this time.
29080      * @method clearConstraints
29081      */
29082     clearConstraints: function() {
29083         this.constrainX = false;
29084         this.constrainY = false;
29085         this.clearTicks();
29086     },
29087
29088     /**
29089      * Clears any tick interval defined for this instance
29090      * @method clearTicks
29091      */
29092     clearTicks: function() {
29093         this.xTicks = null;
29094         this.yTicks = null;
29095         this.xTickSize = 0;
29096         this.yTickSize = 0;
29097     },
29098
29099     /**
29100      * By default, the element can be dragged any place on the screen.  Set
29101      * this to limit the vertical travel of the element.  Pass in 0,0 for the
29102      * parameters if you want to lock the drag to the x axis.
29103      * @method setYConstraint
29104      * @param {int} iUp the number of pixels the element can move up
29105      * @param {int} iDown the number of pixels the element can move down
29106      * @param {int} iTickSize optional parameter for specifying that the
29107      * element should move iTickSize pixels at a time.
29108      */
29109     setYConstraint: function(iUp, iDown, iTickSize) {
29110         this.topConstraint = iUp;
29111         this.bottomConstraint = iDown;
29112
29113         this.minY = this.initPageY - iUp;
29114         this.maxY = this.initPageY + iDown;
29115         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
29116
29117         this.constrainY = true;
29118
29119     },
29120
29121     /**
29122      * resetConstraints must be called if you manually reposition a dd element.
29123      * @method resetConstraints
29124      * @param {boolean} maintainOffset
29125      */
29126     resetConstraints: function() {
29127         // Maintain offsets if necessary
29128         if (this.initPageX || this.initPageX === 0) {
29129             // figure out how much this thing has moved
29130             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
29131             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
29132
29133             this.setInitPosition(dx, dy);
29134
29135         // This is the first time we have detected the element's position
29136         } else {
29137             this.setInitPosition();
29138         }
29139
29140         if (this.constrainX) {
29141             this.setXConstraint( this.leftConstraint,
29142                                  this.rightConstraint,
29143                                  this.xTickSize        );
29144         }
29145
29146         if (this.constrainY) {
29147             this.setYConstraint( this.topConstraint,
29148                                  this.bottomConstraint,
29149                                  this.yTickSize         );
29150         }
29151     },
29152
29153     /**
29154      * Normally the drag element is moved pixel by pixel, but we can specify
29155      * that it move a number of pixels at a time.  This method resolves the
29156      * location when we have it set up like this.
29157      * @method getTick
29158      * @param {int} val where we want to place the object
29159      * @param {int[]} tickArray sorted array of valid points
29160      * @return {int} the closest tick
29161      * @private
29162      */
29163     getTick: function(val, tickArray) {
29164         if (!tickArray) {
29165             // If tick interval is not defined, it is effectively 1 pixel,
29166             // so we return the value passed to us.
29167             return val;
29168         } else if (tickArray[0] >= val) {
29169             // The value is lower than the first tick, so we return the first
29170             // tick.
29171             return tickArray[0];
29172         } else {
29173             for (var i=0, len=tickArray.length; i<len; ++i) {
29174                 var next = i + 1;
29175                 if (tickArray[next] && tickArray[next] >= val) {
29176                     var diff1 = val - tickArray[i];
29177                     var diff2 = tickArray[next] - val;
29178                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
29179                 }
29180             }
29181
29182             // The value is larger than the last tick, so we return the last
29183             // tick.
29184             return tickArray[tickArray.length - 1];
29185         }
29186     },
29187
29188     /**
29189      * toString method
29190      * @method toString
29191      * @return {string} string representation of the dd obj
29192      */
29193     toString: function() {
29194         return ("DragDrop " + this.id);
29195     }
29196
29197 };
29198
29199 })();
29200 /*!
29201  * The drag and drop utility provides a framework for building drag and drop
29202  * applications.  In addition to enabling drag and drop for specific elements,
29203  * the drag and drop elements are tracked by the manager class, and the
29204  * interactions between the various elements are tracked during the drag and
29205  * the implementing code is notified about these important moments.
29206  */
29207
29208 // Only load the library once.  Rewriting the manager class would orphan
29209 // existing drag and drop instances.
29210 if (!Ext.dd.DragDropMgr) {
29211
29212 /**
29213  * @class Ext.dd.DragDropMgr
29214  * DragDropMgr is a singleton that tracks the element interaction for
29215  * all DragDrop items in the window.  Generally, you will not call
29216  * this class directly, but it does have helper methods that could
29217  * be useful in your DragDrop implementations.
29218  * @singleton
29219  */
29220 Ext.dd.DragDropMgr = function() {
29221
29222     var Event = Ext.EventManager;
29223
29224     return {
29225
29226         /**
29227          * Two dimensional Array of registered DragDrop objects.  The first
29228          * dimension is the DragDrop item group, the second the DragDrop
29229          * object.
29230          * @property ids
29231          * @type String[]
29232          * @private
29233          * @static
29234          */
29235         ids: {},
29236
29237         /**
29238          * Array of element ids defined as drag handles.  Used to determine
29239          * if the element that generated the mousedown event is actually the
29240          * handle and not the html element itself.
29241          * @property handleIds
29242          * @type String[]
29243          * @private
29244          * @static
29245          */
29246         handleIds: {},
29247
29248         /**
29249          * the DragDrop object that is currently being dragged
29250          * @property dragCurrent
29251          * @type DragDrop
29252          * @private
29253          * @static
29254          **/
29255         dragCurrent: null,
29256
29257         /**
29258          * the DragDrop object(s) that are being hovered over
29259          * @property dragOvers
29260          * @type Array
29261          * @private
29262          * @static
29263          */
29264         dragOvers: {},
29265
29266         /**
29267          * the X distance between the cursor and the object being dragged
29268          * @property deltaX
29269          * @type int
29270          * @private
29271          * @static
29272          */
29273         deltaX: 0,
29274
29275         /**
29276          * the Y distance between the cursor and the object being dragged
29277          * @property deltaY
29278          * @type int
29279          * @private
29280          * @static
29281          */
29282         deltaY: 0,
29283
29284         /**
29285          * Flag to determine if we should prevent the default behavior of the
29286          * events we define. By default this is true, but this can be set to
29287          * false if you need the default behavior (not recommended)
29288          * @property preventDefault
29289          * @type boolean
29290          * @static
29291          */
29292         preventDefault: true,
29293
29294         /**
29295          * Flag to determine if we should stop the propagation of the events
29296          * we generate. This is true by default but you may want to set it to
29297          * false if the html element contains other features that require the
29298          * mouse click.
29299          * @property stopPropagation
29300          * @type boolean
29301          * @static
29302          */
29303         stopPropagation: true,
29304
29305         /**
29306          * Internal flag that is set to true when drag and drop has been
29307          * intialized
29308          * @property initialized
29309          * @private
29310          * @static
29311          */
29312         initialized: false,
29313
29314         /**
29315          * All drag and drop can be disabled.
29316          * @property locked
29317          * @private
29318          * @static
29319          */
29320         locked: false,
29321
29322         /**
29323          * Called the first time an element is registered.
29324          * @method init
29325          * @private
29326          * @static
29327          */
29328         init: function() {
29329             this.initialized = true;
29330         },
29331
29332         /**
29333          * In point mode, drag and drop interaction is defined by the
29334          * location of the cursor during the drag/drop
29335          * @property POINT
29336          * @type int
29337          * @static
29338          */
29339         POINT: 0,
29340
29341         /**
29342          * In intersect mode, drag and drop interaction is defined by the
29343          * overlap of two or more drag and drop objects.
29344          * @property INTERSECT
29345          * @type int
29346          * @static
29347          */
29348         INTERSECT: 1,
29349
29350         /**
29351          * The current drag and drop mode.  Default: POINT
29352          * @property mode
29353          * @type int
29354          * @static
29355          */
29356         mode: 0,
29357
29358         /**
29359          * Runs method on all drag and drop objects
29360          * @method _execOnAll
29361          * @private
29362          * @static
29363          */
29364         _execOnAll: function(sMethod, args) {
29365             for (var i in this.ids) {
29366                 for (var j in this.ids[i]) {
29367                     var oDD = this.ids[i][j];
29368                     if (! this.isTypeOfDD(oDD)) {
29369                         continue;
29370                     }
29371                     oDD[sMethod].apply(oDD, args);
29372                 }
29373             }
29374         },
29375
29376         /**
29377          * Drag and drop initialization.  Sets up the global event handlers
29378          * @method _onLoad
29379          * @private
29380          * @static
29381          */
29382         _onLoad: function() {
29383
29384             this.init();
29385
29386
29387             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
29388             Event.on(document, "mousemove", this.handleMouseMove, this, true);
29389             Event.on(window,   "unload",    this._onUnload, this, true);
29390             Event.on(window,   "resize",    this._onResize, this, true);
29391             // Event.on(window,   "mouseout",    this._test);
29392
29393         },
29394
29395         /**
29396          * Reset constraints on all drag and drop objs
29397          * @method _onResize
29398          * @private
29399          * @static
29400          */
29401         _onResize: function(e) {
29402             this._execOnAll("resetConstraints", []);
29403         },
29404
29405         /**
29406          * Lock all drag and drop functionality
29407          * @method lock
29408          * @static
29409          */
29410         lock: function() { this.locked = true; },
29411
29412         /**
29413          * Unlock all drag and drop functionality
29414          * @method unlock
29415          * @static
29416          */
29417         unlock: function() { this.locked = false; },
29418
29419         /**
29420          * Is drag and drop locked?
29421          * @method isLocked
29422          * @return {boolean} True if drag and drop is locked, false otherwise.
29423          * @static
29424          */
29425         isLocked: function() { return this.locked; },
29426
29427         /**
29428          * Location cache that is set for all drag drop objects when a drag is
29429          * initiated, cleared when the drag is finished.
29430          * @property locationCache
29431          * @private
29432          * @static
29433          */
29434         locationCache: {},
29435
29436         /**
29437          * Set useCache to false if you want to force object the lookup of each
29438          * drag and drop linked element constantly during a drag.
29439          * @property useCache
29440          * @type boolean
29441          * @static
29442          */
29443         useCache: true,
29444
29445         /**
29446          * The number of pixels that the mouse needs to move after the
29447          * mousedown before the drag is initiated.  Default=3;
29448          * @property clickPixelThresh
29449          * @type int
29450          * @static
29451          */
29452         clickPixelThresh: 3,
29453
29454         /**
29455          * The number of milliseconds after the mousedown event to initiate the
29456          * drag if we don't get a mouseup event. Default=350
29457          * @property clickTimeThresh
29458          * @type int
29459          * @static
29460          */
29461         clickTimeThresh: 350,
29462
29463         /**
29464          * Flag that indicates that either the drag pixel threshold or the
29465          * mousdown time threshold has been met
29466          * @property dragThreshMet
29467          * @type boolean
29468          * @private
29469          * @static
29470          */
29471         dragThreshMet: false,
29472
29473         /**
29474          * Timeout used for the click time threshold
29475          * @property clickTimeout
29476          * @type Object
29477          * @private
29478          * @static
29479          */
29480         clickTimeout: null,
29481
29482         /**
29483          * The X position of the mousedown event stored for later use when a
29484          * drag threshold is met.
29485          * @property startX
29486          * @type int
29487          * @private
29488          * @static
29489          */
29490         startX: 0,
29491
29492         /**
29493          * The Y position of the mousedown event stored for later use when a
29494          * drag threshold is met.
29495          * @property startY
29496          * @type int
29497          * @private
29498          * @static
29499          */
29500         startY: 0,
29501
29502         /**
29503          * Each DragDrop instance must be registered with the DragDropMgr.
29504          * This is executed in DragDrop.init()
29505          * @method regDragDrop
29506          * @param {DragDrop} oDD the DragDrop object to register
29507          * @param {String} sGroup the name of the group this element belongs to
29508          * @static
29509          */
29510         regDragDrop: function(oDD, sGroup) {
29511             if (!this.initialized) { this.init(); }
29512
29513             if (!this.ids[sGroup]) {
29514                 this.ids[sGroup] = {};
29515             }
29516             this.ids[sGroup][oDD.id] = oDD;
29517         },
29518
29519         /**
29520          * Removes the supplied dd instance from the supplied group. Executed
29521          * by DragDrop.removeFromGroup, so don't call this function directly.
29522          * @method removeDDFromGroup
29523          * @private
29524          * @static
29525          */
29526         removeDDFromGroup: function(oDD, sGroup) {
29527             if (!this.ids[sGroup]) {
29528                 this.ids[sGroup] = {};
29529             }
29530
29531             var obj = this.ids[sGroup];
29532             if (obj && obj[oDD.id]) {
29533                 delete obj[oDD.id];
29534             }
29535         },
29536
29537         /**
29538          * Unregisters a drag and drop item.  This is executed in
29539          * DragDrop.unreg, use that method instead of calling this directly.
29540          * @method _remove
29541          * @private
29542          * @static
29543          */
29544         _remove: function(oDD) {
29545             for (var g in oDD.groups) {
29546                 if (g && this.ids[g] && this.ids[g][oDD.id]) {
29547                     delete this.ids[g][oDD.id];
29548                 }
29549             }
29550             delete this.handleIds[oDD.id];
29551         },
29552
29553         /**
29554          * Each DragDrop handle element must be registered.  This is done
29555          * automatically when executing DragDrop.setHandleElId()
29556          * @method regHandle
29557          * @param {String} sDDId the DragDrop id this element is a handle for
29558          * @param {String} sHandleId the id of the element that is the drag
29559          * handle
29560          * @static
29561          */
29562         regHandle: function(sDDId, sHandleId) {
29563             if (!this.handleIds[sDDId]) {
29564                 this.handleIds[sDDId] = {};
29565             }
29566             this.handleIds[sDDId][sHandleId] = sHandleId;
29567         },
29568
29569         /**
29570          * Utility function to determine if a given element has been
29571          * registered as a drag drop item.
29572          * @method isDragDrop
29573          * @param {String} id the element id to check
29574          * @return {boolean} true if this element is a DragDrop item,
29575          * false otherwise
29576          * @static
29577          */
29578         isDragDrop: function(id) {
29579             return ( this.getDDById(id) ) ? true : false;
29580         },
29581
29582         /**
29583          * Returns the drag and drop instances that are in all groups the
29584          * passed in instance belongs to.
29585          * @method getRelated
29586          * @param {DragDrop} p_oDD the obj to get related data for
29587          * @param {boolean} bTargetsOnly if true, only return targetable objs
29588          * @return {DragDrop[]} the related instances
29589          * @static
29590          */
29591         getRelated: function(p_oDD, bTargetsOnly) {
29592             var oDDs = [];
29593             for (var i in p_oDD.groups) {
29594                 for (var j in this.ids[i]) {
29595                     var dd = this.ids[i][j];
29596                     if (! this.isTypeOfDD(dd)) {
29597                         continue;
29598                     }
29599                     if (!bTargetsOnly || dd.isTarget) {
29600                         oDDs[oDDs.length] = dd;
29601                     }
29602                 }
29603             }
29604
29605             return oDDs;
29606         },
29607
29608         /**
29609          * Returns true if the specified dd target is a legal target for
29610          * the specifice drag obj
29611          * @method isLegalTarget
29612          * @param {DragDrop} oDD the drag obj
29613          * @param {DragDrop} oTargetDD the target
29614          * @return {boolean} true if the target is a legal target for the
29615          * dd obj
29616          * @static
29617          */
29618         isLegalTarget: function (oDD, oTargetDD) {
29619             var targets = this.getRelated(oDD, true);
29620             for (var i=0, len=targets.length;i<len;++i) {
29621                 if (targets[i].id == oTargetDD.id) {
29622                     return true;
29623                 }
29624             }
29625
29626             return false;
29627         },
29628
29629         /**
29630          * My goal is to be able to transparently determine if an object is
29631          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
29632          * returns "object", oDD.constructor.toString() always returns
29633          * "DragDrop" and not the name of the subclass.  So for now it just
29634          * evaluates a well-known variable in DragDrop.
29635          * @method isTypeOfDD
29636          * @param {Object} the object to evaluate
29637          * @return {boolean} true if typeof oDD = DragDrop
29638          * @static
29639          */
29640         isTypeOfDD: function (oDD) {
29641             return (oDD && oDD.__ygDragDrop);
29642         },
29643
29644         /**
29645          * Utility function to determine if a given element has been
29646          * registered as a drag drop handle for the given Drag Drop object.
29647          * @method isHandle
29648          * @param {String} id the element id to check
29649          * @return {boolean} true if this element is a DragDrop handle, false
29650          * otherwise
29651          * @static
29652          */
29653         isHandle: function(sDDId, sHandleId) {
29654             return ( this.handleIds[sDDId] &&
29655                             this.handleIds[sDDId][sHandleId] );
29656         },
29657
29658         /**
29659          * Returns the DragDrop instance for a given id
29660          * @method getDDById
29661          * @param {String} id the id of the DragDrop object
29662          * @return {DragDrop} the drag drop object, null if it is not found
29663          * @static
29664          */
29665         getDDById: function(id) {
29666             for (var i in this.ids) {
29667                 if (this.ids[i][id]) {
29668                     return this.ids[i][id];
29669                 }
29670             }
29671             return null;
29672         },
29673
29674         /**
29675          * Fired after a registered DragDrop object gets the mousedown event.
29676          * Sets up the events required to track the object being dragged
29677          * @method handleMouseDown
29678          * @param {Event} e the event
29679          * @param oDD the DragDrop object being dragged
29680          * @private
29681          * @static
29682          */
29683         handleMouseDown: function(e, oDD) {
29684             if(Ext.QuickTips){
29685                 Ext.QuickTips.disable();
29686             }
29687             if(this.dragCurrent){
29688                 // the original browser mouseup wasn't handled (e.g. outside FF browser window)
29689                 // so clean up first to avoid breaking the next drag
29690                 this.handleMouseUp(e);
29691             }
29692             
29693             this.currentTarget = e.getTarget();
29694             this.dragCurrent = oDD;
29695
29696             var el = oDD.getEl();
29697
29698             // track start position
29699             this.startX = e.getPageX();
29700             this.startY = e.getPageY();
29701
29702             this.deltaX = this.startX - el.offsetLeft;
29703             this.deltaY = this.startY - el.offsetTop;
29704
29705             this.dragThreshMet = false;
29706
29707             this.clickTimeout = setTimeout(
29708                     function() {
29709                         var DDM = Ext.dd.DDM;
29710                         DDM.startDrag(DDM.startX, DDM.startY);
29711                     },
29712                     this.clickTimeThresh );
29713         },
29714
29715         /**
29716          * Fired when either the drag pixel threshol or the mousedown hold
29717          * time threshold has been met.
29718          * @method startDrag
29719          * @param x {int} the X position of the original mousedown
29720          * @param y {int} the Y position of the original mousedown
29721          * @static
29722          */
29723         startDrag: function(x, y) {
29724             clearTimeout(this.clickTimeout);
29725             if (this.dragCurrent) {
29726                 this.dragCurrent.b4StartDrag(x, y);
29727                 this.dragCurrent.startDrag(x, y);
29728             }
29729             this.dragThreshMet = true;
29730         },
29731
29732         /**
29733          * Internal function to handle the mouseup event.  Will be invoked
29734          * from the context of the document.
29735          * @method handleMouseUp
29736          * @param {Event} e the event
29737          * @private
29738          * @static
29739          */
29740         handleMouseUp: function(e) {
29741
29742             if(Ext.QuickTips){
29743                 Ext.QuickTips.enable();
29744             }
29745             if (! this.dragCurrent) {
29746                 return;
29747             }
29748
29749             clearTimeout(this.clickTimeout);
29750
29751             if (this.dragThreshMet) {
29752                 this.fireEvents(e, true);
29753             } else {
29754             }
29755
29756             this.stopDrag(e);
29757
29758             this.stopEvent(e);
29759         },
29760
29761         /**
29762          * Utility to stop event propagation and event default, if these
29763          * features are turned on.
29764          * @method stopEvent
29765          * @param {Event} e the event as returned by this.getEvent()
29766          * @static
29767          */
29768         stopEvent: function(e){
29769             if(this.stopPropagation) {
29770                 e.stopPropagation();
29771             }
29772
29773             if (this.preventDefault) {
29774                 e.preventDefault();
29775             }
29776         },
29777
29778         /**
29779          * Internal function to clean up event handlers after the drag
29780          * operation is complete
29781          * @method stopDrag
29782          * @param {Event} e the event
29783          * @private
29784          * @static
29785          */
29786         stopDrag: function(e) {
29787             // Fire the drag end event for the item that was dragged
29788             if (this.dragCurrent) {
29789                 if (this.dragThreshMet) {
29790                     this.dragCurrent.b4EndDrag(e);
29791                     this.dragCurrent.endDrag(e);
29792                 }
29793
29794                 this.dragCurrent.onMouseUp(e);
29795             }
29796
29797             this.dragCurrent = null;
29798             this.dragOvers = {};
29799         },
29800
29801         /**
29802          * Internal function to handle the mousemove event.  Will be invoked
29803          * from the context of the html element.
29804          *
29805          * @TODO figure out what we can do about mouse events lost when the
29806          * user drags objects beyond the window boundary.  Currently we can
29807          * detect this in internet explorer by verifying that the mouse is
29808          * down during the mousemove event.  Firefox doesn't give us the
29809          * button state on the mousemove event.
29810          * @method handleMouseMove
29811          * @param {Event} e the event
29812          * @private
29813          * @static
29814          */
29815         handleMouseMove: function(e) {
29816             if (! this.dragCurrent) {
29817                 return true;
29818             }
29819             // var button = e.which || e.button;
29820
29821             // check for IE mouseup outside of page boundary
29822             if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
29823                 this.stopEvent(e);
29824                 return this.handleMouseUp(e);
29825             }
29826
29827             if (!this.dragThreshMet) {
29828                 var diffX = Math.abs(this.startX - e.getPageX());
29829                 var diffY = Math.abs(this.startY - e.getPageY());
29830                 if (diffX > this.clickPixelThresh ||
29831                             diffY > this.clickPixelThresh) {
29832                     this.startDrag(this.startX, this.startY);
29833                 }
29834             }
29835
29836             if (this.dragThreshMet) {
29837                 this.dragCurrent.b4Drag(e);
29838                 this.dragCurrent.onDrag(e);
29839                 if(!this.dragCurrent.moveOnly){
29840                     this.fireEvents(e, false);
29841                 }
29842             }
29843
29844             this.stopEvent(e);
29845
29846             return true;
29847         },
29848
29849         /**
29850          * Iterates over all of the DragDrop elements to find ones we are
29851          * hovering over or dropping on
29852          * @method fireEvents
29853          * @param {Event} e the event
29854          * @param {boolean} isDrop is this a drop op or a mouseover op?
29855          * @private
29856          * @static
29857          */
29858         fireEvents: function(e, isDrop) {
29859             var dc = this.dragCurrent;
29860
29861             // If the user did the mouse up outside of the window, we could
29862             // get here even though we have ended the drag.
29863             if (!dc || dc.isLocked()) {
29864                 return;
29865             }
29866
29867             var pt = e.getPoint();
29868
29869             // cache the previous dragOver array
29870             var oldOvers = [];
29871
29872             var outEvts   = [];
29873             var overEvts  = [];
29874             var dropEvts  = [];
29875             var enterEvts = [];
29876
29877             // Check to see if the object(s) we were hovering over is no longer
29878             // being hovered over so we can fire the onDragOut event
29879             for (var i in this.dragOvers) {
29880
29881                 var ddo = this.dragOvers[i];
29882
29883                 if (! this.isTypeOfDD(ddo)) {
29884                     continue;
29885                 }
29886
29887                 if (! this.isOverTarget(pt, ddo, this.mode)) {
29888                     outEvts.push( ddo );
29889                 }
29890
29891                 oldOvers[i] = true;
29892                 delete this.dragOvers[i];
29893             }
29894
29895             for (var sGroup in dc.groups) {
29896
29897                 if ("string" != typeof sGroup) {
29898                     continue;
29899                 }
29900
29901                 for (i in this.ids[sGroup]) {
29902                     var oDD = this.ids[sGroup][i];
29903                     if (! this.isTypeOfDD(oDD)) {
29904                         continue;
29905                     }
29906
29907                     if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
29908                         if (this.isOverTarget(pt, oDD, this.mode)) {
29909                             // look for drop interactions
29910                             if (isDrop) {
29911                                 dropEvts.push( oDD );
29912                             // look for drag enter and drag over interactions
29913                             } else {
29914
29915                                 // initial drag over: dragEnter fires
29916                                 if (!oldOvers[oDD.id]) {
29917                                     enterEvts.push( oDD );
29918                                 // subsequent drag overs: dragOver fires
29919                                 } else {
29920                                     overEvts.push( oDD );
29921                                 }
29922
29923                                 this.dragOvers[oDD.id] = oDD;
29924                             }
29925                         }
29926                     }
29927                 }
29928             }
29929
29930             if (this.mode) {
29931                 if (outEvts.length) {
29932                     dc.b4DragOut(e, outEvts);
29933                     dc.onDragOut(e, outEvts);
29934                 }
29935
29936                 if (enterEvts.length) {
29937                     dc.onDragEnter(e, enterEvts);
29938                 }
29939
29940                 if (overEvts.length) {
29941                     dc.b4DragOver(e, overEvts);
29942                     dc.onDragOver(e, overEvts);
29943                 }
29944
29945                 if (dropEvts.length) {
29946                     dc.b4DragDrop(e, dropEvts);
29947                     dc.onDragDrop(e, dropEvts);
29948                 }
29949
29950             } else {
29951                 // fire dragout events
29952                 var len = 0;
29953                 for (i=0, len=outEvts.length; i<len; ++i) {
29954                     dc.b4DragOut(e, outEvts[i].id);
29955                     dc.onDragOut(e, outEvts[i].id);
29956                 }
29957
29958                 // fire enter events
29959                 for (i=0,len=enterEvts.length; i<len; ++i) {
29960                     // dc.b4DragEnter(e, oDD.id);
29961                     dc.onDragEnter(e, enterEvts[i].id);
29962                 }
29963
29964                 // fire over events
29965                 for (i=0,len=overEvts.length; i<len; ++i) {
29966                     dc.b4DragOver(e, overEvts[i].id);
29967                     dc.onDragOver(e, overEvts[i].id);
29968                 }
29969
29970                 // fire drop events
29971                 for (i=0, len=dropEvts.length; i<len; ++i) {
29972                     dc.b4DragDrop(e, dropEvts[i].id);
29973                     dc.onDragDrop(e, dropEvts[i].id);
29974                 }
29975
29976             }
29977
29978             // notify about a drop that did not find a target
29979             if (isDrop && !dropEvts.length) {
29980                 dc.onInvalidDrop(e);
29981             }
29982
29983         },
29984
29985         /**
29986          * Helper function for getting the best match from the list of drag
29987          * and drop objects returned by the drag and drop events when we are
29988          * in INTERSECT mode.  It returns either the first object that the
29989          * cursor is over, or the object that has the greatest overlap with
29990          * the dragged element.
29991          * @method getBestMatch
29992          * @param  {DragDrop[]} dds The array of drag and drop objects
29993          * targeted
29994          * @return {DragDrop}       The best single match
29995          * @static
29996          */
29997         getBestMatch: function(dds) {
29998             var winner = null;
29999             // Return null if the input is not what we expect
30000             //if (!dds || !dds.length || dds.length == 0) {
30001                // winner = null;
30002             // If there is only one item, it wins
30003             //} else if (dds.length == 1) {
30004
30005             var len = dds.length;
30006
30007             if (len == 1) {
30008                 winner = dds[0];
30009             } else {
30010                 // Loop through the targeted items
30011                 for (var i=0; i<len; ++i) {
30012                     var dd = dds[i];
30013                     // If the cursor is over the object, it wins.  If the
30014                     // cursor is over multiple matches, the first one we come
30015                     // to wins.
30016                     if (dd.cursorIsOver) {
30017                         winner = dd;
30018                         break;
30019                     // Otherwise the object with the most overlap wins
30020                     } else {
30021                         if (!winner ||
30022                             winner.overlap.getArea() < dd.overlap.getArea()) {
30023                             winner = dd;
30024                         }
30025                     }
30026                 }
30027             }
30028
30029             return winner;
30030         },
30031
30032         /**
30033          * Refreshes the cache of the top-left and bottom-right points of the
30034          * drag and drop objects in the specified group(s).  This is in the
30035          * format that is stored in the drag and drop instance, so typical
30036          * usage is:
30037          * <code>
30038          * Ext.dd.DragDropMgr.refreshCache(ddinstance.groups);
30039          * </code>
30040          * Alternatively:
30041          * <code>
30042          * Ext.dd.DragDropMgr.refreshCache({group1:true, group2:true});
30043          * </code>
30044          * @TODO this really should be an indexed array.  Alternatively this
30045          * method could accept both.
30046          * @method refreshCache
30047          * @param {Object} groups an associative array of groups to refresh
30048          * @static
30049          */
30050         refreshCache: function(groups) {
30051             for (var sGroup in groups) {
30052                 if ("string" != typeof sGroup) {
30053                     continue;
30054                 }
30055                 for (var i in this.ids[sGroup]) {
30056                     var oDD = this.ids[sGroup][i];
30057
30058                     if (this.isTypeOfDD(oDD)) {
30059                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
30060                         var loc = this.getLocation(oDD);
30061                         if (loc) {
30062                             this.locationCache[oDD.id] = loc;
30063                         } else {
30064                             delete this.locationCache[oDD.id];
30065                             // this will unregister the drag and drop object if
30066                             // the element is not in a usable state
30067                             // oDD.unreg();
30068                         }
30069                     }
30070                 }
30071             }
30072         },
30073
30074         /**
30075          * This checks to make sure an element exists and is in the DOM.  The
30076          * main purpose is to handle cases where innerHTML is used to remove
30077          * drag and drop objects from the DOM.  IE provides an 'unspecified
30078          * error' when trying to access the offsetParent of such an element
30079          * @method verifyEl
30080          * @param {HTMLElement} el the element to check
30081          * @return {boolean} true if the element looks usable
30082          * @static
30083          */
30084         verifyEl: function(el) {
30085             if (el) {
30086                 var parent;
30087                 if(Ext.isIE){
30088                     try{
30089                         parent = el.offsetParent;
30090                     }catch(e){}
30091                 }else{
30092                     parent = el.offsetParent;
30093                 }
30094                 if (parent) {
30095                     return true;
30096                 }
30097             }
30098
30099             return false;
30100         },
30101
30102         /**
30103          * Returns a Region object containing the drag and drop element's position
30104          * and size, including the padding configured for it
30105          * @method getLocation
30106          * @param {DragDrop} oDD the drag and drop object to get the
30107          *                       location for
30108          * @return {Ext.lib.Region} a Region object representing the total area
30109          *                             the element occupies, including any padding
30110          *                             the instance is configured for.
30111          * @static
30112          */
30113         getLocation: function(oDD) {
30114             if (! this.isTypeOfDD(oDD)) {
30115                 return null;
30116             }
30117
30118             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
30119
30120             try {
30121                 pos= Ext.lib.Dom.getXY(el);
30122             } catch (e) { }
30123
30124             if (!pos) {
30125                 return null;
30126             }
30127
30128             x1 = pos[0];
30129             x2 = x1 + el.offsetWidth;
30130             y1 = pos[1];
30131             y2 = y1 + el.offsetHeight;
30132
30133             t = y1 - oDD.padding[0];
30134             r = x2 + oDD.padding[1];
30135             b = y2 + oDD.padding[2];
30136             l = x1 - oDD.padding[3];
30137
30138             return new Ext.lib.Region( t, r, b, l );
30139         },
30140
30141         /**
30142          * Checks the cursor location to see if it over the target
30143          * @method isOverTarget
30144          * @param {Ext.lib.Point} pt The point to evaluate
30145          * @param {DragDrop} oTarget the DragDrop object we are inspecting
30146          * @return {boolean} true if the mouse is over the target
30147          * @private
30148          * @static
30149          */
30150         isOverTarget: function(pt, oTarget, intersect) {
30151             // use cache if available
30152             var loc = this.locationCache[oTarget.id];
30153             if (!loc || !this.useCache) {
30154                 loc = this.getLocation(oTarget);
30155                 this.locationCache[oTarget.id] = loc;
30156
30157             }
30158
30159             if (!loc) {
30160                 return false;
30161             }
30162
30163             oTarget.cursorIsOver = loc.contains( pt );
30164
30165             // DragDrop is using this as a sanity check for the initial mousedown
30166             // in this case we are done.  In POINT mode, if the drag obj has no
30167             // contraints, we are also done. Otherwise we need to evaluate the
30168             // location of the target as related to the actual location of the
30169             // dragged element.
30170             var dc = this.dragCurrent;
30171             if (!dc || !dc.getTargetCoord ||
30172                     (!intersect && !dc.constrainX && !dc.constrainY)) {
30173                 return oTarget.cursorIsOver;
30174             }
30175
30176             oTarget.overlap = null;
30177
30178             // Get the current location of the drag element, this is the
30179             // location of the mouse event less the delta that represents
30180             // where the original mousedown happened on the element.  We
30181             // need to consider constraints and ticks as well.
30182             var pos = dc.getTargetCoord(pt.x, pt.y);
30183
30184             var el = dc.getDragEl();
30185             var curRegion = new Ext.lib.Region( pos.y,
30186                                                    pos.x + el.offsetWidth,
30187                                                    pos.y + el.offsetHeight,
30188                                                    pos.x );
30189
30190             var overlap = curRegion.intersect(loc);
30191
30192             if (overlap) {
30193                 oTarget.overlap = overlap;
30194                 return (intersect) ? true : oTarget.cursorIsOver;
30195             } else {
30196                 return false;
30197             }
30198         },
30199
30200         /**
30201          * unload event handler
30202          * @method _onUnload
30203          * @private
30204          * @static
30205          */
30206         _onUnload: function(e, me) {
30207             Ext.dd.DragDropMgr.unregAll();
30208         },
30209
30210         /**
30211          * Cleans up the drag and drop events and objects.
30212          * @method unregAll
30213          * @private
30214          * @static
30215          */
30216         unregAll: function() {
30217
30218             if (this.dragCurrent) {
30219                 this.stopDrag();
30220                 this.dragCurrent = null;
30221             }
30222
30223             this._execOnAll("unreg", []);
30224
30225             for (var i in this.elementCache) {
30226                 delete this.elementCache[i];
30227             }
30228
30229             this.elementCache = {};
30230             this.ids = {};
30231         },
30232
30233         /**
30234          * A cache of DOM elements
30235          * @property elementCache
30236          * @private
30237          * @static
30238          */
30239         elementCache: {},
30240
30241         /**
30242          * Get the wrapper for the DOM element specified
30243          * @method getElWrapper
30244          * @param {String} id the id of the element to get
30245          * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
30246          * @private
30247          * @deprecated This wrapper isn't that useful
30248          * @static
30249          */
30250         getElWrapper: function(id) {
30251             var oWrapper = this.elementCache[id];
30252             if (!oWrapper || !oWrapper.el) {
30253                 oWrapper = this.elementCache[id] =
30254                     new this.ElementWrapper(Ext.getDom(id));
30255             }
30256             return oWrapper;
30257         },
30258
30259         /**
30260          * Returns the actual DOM element
30261          * @method getElement
30262          * @param {String} id the id of the elment to get
30263          * @return {Object} The element
30264          * @deprecated use Ext.lib.Ext.getDom instead
30265          * @static
30266          */
30267         getElement: function(id) {
30268             return Ext.getDom(id);
30269         },
30270
30271         /**
30272          * Returns the style property for the DOM element (i.e.,
30273          * document.getElById(id).style)
30274          * @method getCss
30275          * @param {String} id the id of the elment to get
30276          * @return {Object} The style property of the element
30277          * @deprecated use Ext.lib.Dom instead
30278          * @static
30279          */
30280         getCss: function(id) {
30281             var el = Ext.getDom(id);
30282             return (el) ? el.style : null;
30283         },
30284
30285         /**
30286          * Inner class for cached elements
30287          * @class Ext.dd.DragDropMgr.ElementWrapper
30288          * @for DragDropMgr
30289          * @private
30290          * @deprecated
30291          */
30292         ElementWrapper: function(el) {
30293                 /**
30294                  * The element
30295                  * @property el
30296                  */
30297                 this.el = el || null;
30298                 /**
30299                  * The element id
30300                  * @property id
30301                  */
30302                 this.id = this.el && el.id;
30303                 /**
30304                  * A reference to the style property
30305                  * @property css
30306                  */
30307                 this.css = this.el && el.style;
30308             },
30309
30310         /**
30311          * Returns the X position of an html element
30312          * @method getPosX
30313          * @param el the element for which to get the position
30314          * @return {int} the X coordinate
30315          * @for DragDropMgr
30316          * @deprecated use Ext.lib.Dom.getX instead
30317          * @static
30318          */
30319         getPosX: function(el) {
30320             return Ext.lib.Dom.getX(el);
30321         },
30322
30323         /**
30324          * Returns the Y position of an html element
30325          * @method getPosY
30326          * @param el the element for which to get the position
30327          * @return {int} the Y coordinate
30328          * @deprecated use Ext.lib.Dom.getY instead
30329          * @static
30330          */
30331         getPosY: function(el) {
30332             return Ext.lib.Dom.getY(el);
30333         },
30334
30335         /**
30336          * Swap two nodes.  In IE, we use the native method, for others we
30337          * emulate the IE behavior
30338          * @method swapNode
30339          * @param n1 the first node to swap
30340          * @param n2 the other node to swap
30341          * @static
30342          */
30343         swapNode: function(n1, n2) {
30344             if (n1.swapNode) {
30345                 n1.swapNode(n2);
30346             } else {
30347                 var p = n2.parentNode;
30348                 var s = n2.nextSibling;
30349
30350                 if (s == n1) {
30351                     p.insertBefore(n1, n2);
30352                 } else if (n2 == n1.nextSibling) {
30353                     p.insertBefore(n2, n1);
30354                 } else {
30355                     n1.parentNode.replaceChild(n2, n1);
30356                     p.insertBefore(n1, s);
30357                 }
30358             }
30359         },
30360
30361         /**
30362          * Returns the current scroll position
30363          * @method getScroll
30364          * @private
30365          * @static
30366          */
30367         getScroll: function () {
30368             var t, l, dde=document.documentElement, db=document.body;
30369             if (dde && (dde.scrollTop || dde.scrollLeft)) {
30370                 t = dde.scrollTop;
30371                 l = dde.scrollLeft;
30372             } else if (db) {
30373                 t = db.scrollTop;
30374                 l = db.scrollLeft;
30375             } else {
30376
30377             }
30378             return { top: t, left: l };
30379         },
30380
30381         /**
30382          * Returns the specified element style property
30383          * @method getStyle
30384          * @param {HTMLElement} el          the element
30385          * @param {string}      styleProp   the style property
30386          * @return {string} The value of the style property
30387          * @deprecated use Ext.lib.Dom.getStyle
30388          * @static
30389          */
30390         getStyle: function(el, styleProp) {
30391             return Ext.fly(el).getStyle(styleProp);
30392         },
30393
30394         /**
30395          * Gets the scrollTop
30396          * @method getScrollTop
30397          * @return {int} the document's scrollTop
30398          * @static
30399          */
30400         getScrollTop: function () {
30401             return this.getScroll().top;
30402         },
30403
30404         /**
30405          * Gets the scrollLeft
30406          * @method getScrollLeft
30407          * @return {int} the document's scrollTop
30408          * @static
30409          */
30410         getScrollLeft: function () {
30411             return this.getScroll().left;
30412         },
30413
30414         /**
30415          * Sets the x/y position of an element to the location of the
30416          * target element.
30417          * @method moveToEl
30418          * @param {HTMLElement} moveEl      The element to move
30419          * @param {HTMLElement} targetEl    The position reference element
30420          * @static
30421          */
30422         moveToEl: function (moveEl, targetEl) {
30423             var aCoord = Ext.lib.Dom.getXY(targetEl);
30424             Ext.lib.Dom.setXY(moveEl, aCoord);
30425         },
30426
30427         /**
30428          * Numeric array sort function
30429          * @method numericSort
30430          * @static
30431          */
30432         numericSort: function(a, b) {
30433             return (a - b);
30434         },
30435
30436         /**
30437          * Internal counter
30438          * @property _timeoutCount
30439          * @private
30440          * @static
30441          */
30442         _timeoutCount: 0,
30443
30444         /**
30445          * Trying to make the load order less important.  Without this we get
30446          * an error if this file is loaded before the Event Utility.
30447          * @method _addListeners
30448          * @private
30449          * @static
30450          */
30451         _addListeners: function() {
30452             var DDM = Ext.dd.DDM;
30453             if ( Ext.lib.Event && document ) {
30454                 DDM._onLoad();
30455             } else {
30456                 if (DDM._timeoutCount > 2000) {
30457                 } else {
30458                     setTimeout(DDM._addListeners, 10);
30459                     if (document && document.body) {
30460                         DDM._timeoutCount += 1;
30461                     }
30462                 }
30463             }
30464         },
30465
30466         /**
30467          * Recursively searches the immediate parent and all child nodes for
30468          * the handle element in order to determine wheter or not it was
30469          * clicked.
30470          * @method handleWasClicked
30471          * @param node the html element to inspect
30472          * @static
30473          */
30474         handleWasClicked: function(node, id) {
30475             if (this.isHandle(id, node.id)) {
30476                 return true;
30477             } else {
30478                 // check to see if this is a text node child of the one we want
30479                 var p = node.parentNode;
30480
30481                 while (p) {
30482                     if (this.isHandle(id, p.id)) {
30483                         return true;
30484                     } else {
30485                         p = p.parentNode;
30486                     }
30487                 }
30488             }
30489
30490             return false;
30491         }
30492
30493     };
30494
30495 }();
30496
30497 // shorter alias, save a few bytes
30498 Ext.dd.DDM = Ext.dd.DragDropMgr;
30499 Ext.dd.DDM._addListeners();
30500
30501 }
30502
30503 /**
30504  * @class Ext.dd.DD
30505  * A DragDrop implementation where the linked element follows the
30506  * mouse cursor during a drag.
30507  * @extends Ext.dd.DragDrop
30508  * @constructor
30509  * @param {String} id the id of the linked element
30510  * @param {String} sGroup the group of related DragDrop items
30511  * @param {object} config an object containing configurable attributes
30512  *                Valid properties for DD:
30513  *                    scroll
30514  */
30515 Ext.dd.DD = function(id, sGroup, config) {
30516     if (id) {
30517         this.init(id, sGroup, config);
30518     }
30519 };
30520
30521 Ext.extend(Ext.dd.DD, Ext.dd.DragDrop, {
30522
30523     /**
30524      * When set to true, the utility automatically tries to scroll the browser
30525      * window when a drag and drop element is dragged near the viewport boundary.
30526      * Defaults to true.
30527      * @property scroll
30528      * @type boolean
30529      */
30530     scroll: true,
30531
30532     /**
30533      * Sets the pointer offset to the distance between the linked element's top
30534      * left corner and the location the element was clicked
30535      * @method autoOffset
30536      * @param {int} iPageX the X coordinate of the click
30537      * @param {int} iPageY the Y coordinate of the click
30538      */
30539     autoOffset: function(iPageX, iPageY) {
30540         var x = iPageX - this.startPageX;
30541         var y = iPageY - this.startPageY;
30542         this.setDelta(x, y);
30543     },
30544
30545     /**
30546      * Sets the pointer offset.  You can call this directly to force the
30547      * offset to be in a particular location (e.g., pass in 0,0 to set it
30548      * to the center of the object)
30549      * @method setDelta
30550      * @param {int} iDeltaX the distance from the left
30551      * @param {int} iDeltaY the distance from the top
30552      */
30553     setDelta: function(iDeltaX, iDeltaY) {
30554         this.deltaX = iDeltaX;
30555         this.deltaY = iDeltaY;
30556     },
30557
30558     /**
30559      * Sets the drag element to the location of the mousedown or click event,
30560      * maintaining the cursor location relative to the location on the element
30561      * that was clicked.  Override this if you want to place the element in a
30562      * location other than where the cursor is.
30563      * @method setDragElPos
30564      * @param {int} iPageX the X coordinate of the mousedown or drag event
30565      * @param {int} iPageY the Y coordinate of the mousedown or drag event
30566      */
30567     setDragElPos: function(iPageX, iPageY) {
30568         // the first time we do this, we are going to check to make sure
30569         // the element has css positioning
30570
30571         var el = this.getDragEl();
30572         this.alignElWithMouse(el, iPageX, iPageY);
30573     },
30574
30575     /**
30576      * Sets the element to the location of the mousedown or click event,
30577      * maintaining the cursor location relative to the location on the element
30578      * that was clicked.  Override this if you want to place the element in a
30579      * location other than where the cursor is.
30580      * @method alignElWithMouse
30581      * @param {HTMLElement} el the element to move
30582      * @param {int} iPageX the X coordinate of the mousedown or drag event
30583      * @param {int} iPageY the Y coordinate of the mousedown or drag event
30584      */
30585     alignElWithMouse: function(el, iPageX, iPageY) {
30586         var oCoord = this.getTargetCoord(iPageX, iPageY);
30587         var fly = el.dom ? el : Ext.fly(el, '_dd');
30588         if (!this.deltaSetXY) {
30589             var aCoord = [oCoord.x, oCoord.y];
30590             fly.setXY(aCoord);
30591             var newLeft = fly.getLeft(true);
30592             var newTop  = fly.getTop(true);
30593             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
30594         } else {
30595             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
30596         }
30597
30598         this.cachePosition(oCoord.x, oCoord.y);
30599         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
30600         return oCoord;
30601     },
30602
30603     /**
30604      * Saves the most recent position so that we can reset the constraints and
30605      * tick marks on-demand.  We need to know this so that we can calculate the
30606      * number of pixels the element is offset from its original position.
30607      * @method cachePosition
30608      * @param iPageX the current x position (optional, this just makes it so we
30609      * don't have to look it up again)
30610      * @param iPageY the current y position (optional, this just makes it so we
30611      * don't have to look it up again)
30612      */
30613     cachePosition: function(iPageX, iPageY) {
30614         if (iPageX) {
30615             this.lastPageX = iPageX;
30616             this.lastPageY = iPageY;
30617         } else {
30618             var aCoord = Ext.lib.Dom.getXY(this.getEl());
30619             this.lastPageX = aCoord[0];
30620             this.lastPageY = aCoord[1];
30621         }
30622     },
30623
30624     /**
30625      * Auto-scroll the window if the dragged object has been moved beyond the
30626      * visible window boundary.
30627      * @method autoScroll
30628      * @param {int} x the drag element's x position
30629      * @param {int} y the drag element's y position
30630      * @param {int} h the height of the drag element
30631      * @param {int} w the width of the drag element
30632      * @private
30633      */
30634     autoScroll: function(x, y, h, w) {
30635
30636         if (this.scroll) {
30637             // The client height
30638             var clientH = Ext.lib.Dom.getViewHeight();
30639
30640             // The client width
30641             var clientW = Ext.lib.Dom.getViewWidth();
30642
30643             // The amt scrolled down
30644             var st = this.DDM.getScrollTop();
30645
30646             // The amt scrolled right
30647             var sl = this.DDM.getScrollLeft();
30648
30649             // Location of the bottom of the element
30650             var bot = h + y;
30651
30652             // Location of the right of the element
30653             var right = w + x;
30654
30655             // The distance from the cursor to the bottom of the visible area,
30656             // adjusted so that we don't scroll if the cursor is beyond the
30657             // element drag constraints
30658             var toBot = (clientH + st - y - this.deltaY);
30659
30660             // The distance from the cursor to the right of the visible area
30661             var toRight = (clientW + sl - x - this.deltaX);
30662
30663
30664             // How close to the edge the cursor must be before we scroll
30665             // var thresh = (document.all) ? 100 : 40;
30666             var thresh = 40;
30667
30668             // How many pixels to scroll per autoscroll op.  This helps to reduce
30669             // clunky scrolling. IE is more sensitive about this ... it needs this
30670             // value to be higher.
30671             var scrAmt = (document.all) ? 80 : 30;
30672
30673             // Scroll down if we are near the bottom of the visible page and the
30674             // obj extends below the crease
30675             if ( bot > clientH && toBot < thresh ) {
30676                 window.scrollTo(sl, st + scrAmt);
30677             }
30678
30679             // Scroll up if the window is scrolled down and the top of the object
30680             // goes above the top border
30681             if ( y < st && st > 0 && y - st < thresh ) {
30682                 window.scrollTo(sl, st - scrAmt);
30683             }
30684
30685             // Scroll right if the obj is beyond the right border and the cursor is
30686             // near the border.
30687             if ( right > clientW && toRight < thresh ) {
30688                 window.scrollTo(sl + scrAmt, st);
30689             }
30690
30691             // Scroll left if the window has been scrolled to the right and the obj
30692             // extends past the left border
30693             if ( x < sl && sl > 0 && x - sl < thresh ) {
30694                 window.scrollTo(sl - scrAmt, st);
30695             }
30696         }
30697     },
30698
30699     /**
30700      * Finds the location the element should be placed if we want to move
30701      * it to where the mouse location less the click offset would place us.
30702      * @method getTargetCoord
30703      * @param {int} iPageX the X coordinate of the click
30704      * @param {int} iPageY the Y coordinate of the click
30705      * @return an object that contains the coordinates (Object.x and Object.y)
30706      * @private
30707      */
30708     getTargetCoord: function(iPageX, iPageY) {
30709         var x = iPageX - this.deltaX;
30710         var y = iPageY - this.deltaY;
30711
30712         if (this.constrainX) {
30713             if (x < this.minX) { x = this.minX; }
30714             if (x > this.maxX) { x = this.maxX; }
30715         }
30716
30717         if (this.constrainY) {
30718             if (y < this.minY) { y = this.minY; }
30719             if (y > this.maxY) { y = this.maxY; }
30720         }
30721
30722         x = this.getTick(x, this.xTicks);
30723         y = this.getTick(y, this.yTicks);
30724
30725
30726         return {x:x, y:y};
30727     },
30728
30729     /**
30730      * Sets up config options specific to this class. Overrides
30731      * Ext.dd.DragDrop, but all versions of this method through the
30732      * inheritance chain are called
30733      */
30734     applyConfig: function() {
30735         Ext.dd.DD.superclass.applyConfig.call(this);
30736         this.scroll = (this.config.scroll !== false);
30737     },
30738
30739     /**
30740      * Event that fires prior to the onMouseDown event.  Overrides
30741      * Ext.dd.DragDrop.
30742      */
30743     b4MouseDown: function(e) {
30744         // this.resetConstraints();
30745         this.autoOffset(e.getPageX(),
30746                             e.getPageY());
30747     },
30748
30749     /**
30750      * Event that fires prior to the onDrag event.  Overrides
30751      * Ext.dd.DragDrop.
30752      */
30753     b4Drag: function(e) {
30754         this.setDragElPos(e.getPageX(),
30755                             e.getPageY());
30756     },
30757
30758     toString: function() {
30759         return ("DD " + this.id);
30760     }
30761
30762     //////////////////////////////////////////////////////////////////////////
30763     // Debugging ygDragDrop events that can be overridden
30764     //////////////////////////////////////////////////////////////////////////
30765     /*
30766     startDrag: function(x, y) {
30767     },
30768
30769     onDrag: function(e) {
30770     },
30771
30772     onDragEnter: function(e, id) {
30773     },
30774
30775     onDragOver: function(e, id) {
30776     },
30777
30778     onDragOut: function(e, id) {
30779     },
30780
30781     onDragDrop: function(e, id) {
30782     },
30783
30784     endDrag: function(e) {
30785     }
30786
30787     */
30788
30789 });
30790 /**
30791  * @class Ext.dd.DDProxy
30792  * A DragDrop implementation that inserts an empty, bordered div into
30793  * the document that follows the cursor during drag operations.  At the time of
30794  * the click, the frame div is resized to the dimensions of the linked html
30795  * element, and moved to the exact location of the linked element.
30796  *
30797  * References to the "frame" element refer to the single proxy element that
30798  * was created to be dragged in place of all DDProxy elements on the
30799  * page.
30800  *
30801  * @extends Ext.dd.DD
30802  * @constructor
30803  * @param {String} id the id of the linked html element
30804  * @param {String} sGroup the group of related DragDrop objects
30805  * @param {object} config an object containing configurable attributes
30806  *                Valid properties for DDProxy in addition to those in DragDrop:
30807  *                   resizeFrame, centerFrame, dragElId
30808  */
30809 Ext.dd.DDProxy = function(id, sGroup, config) {
30810     if (id) {
30811         this.init(id, sGroup, config);
30812         this.initFrame();
30813     }
30814 };
30815
30816 /**
30817  * The default drag frame div id
30818  * @property Ext.dd.DDProxy.dragElId
30819  * @type String
30820  * @static
30821  */
30822 Ext.dd.DDProxy.dragElId = "ygddfdiv";
30823
30824 Ext.extend(Ext.dd.DDProxy, Ext.dd.DD, {
30825
30826     /**
30827      * By default we resize the drag frame to be the same size as the element
30828      * we want to drag (this is to get the frame effect).  We can turn it off
30829      * if we want a different behavior.
30830      * @property resizeFrame
30831      * @type boolean
30832      */
30833     resizeFrame: true,
30834
30835     /**
30836      * By default the frame is positioned exactly where the drag element is, so
30837      * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
30838      * you do not have constraints on the obj is to have the drag frame centered
30839      * around the cursor.  Set centerFrame to true for this effect.
30840      * @property centerFrame
30841      * @type boolean
30842      */
30843     centerFrame: false,
30844
30845     /**
30846      * Creates the proxy element if it does not yet exist
30847      * @method createFrame
30848      */
30849     createFrame: function() {
30850         var self = this;
30851         var body = document.body;
30852
30853         if (!body || !body.firstChild) {
30854             setTimeout( function() { self.createFrame(); }, 50 );
30855             return;
30856         }
30857
30858         var div = this.getDragEl();
30859
30860         if (!div) {
30861             div    = document.createElement("div");
30862             div.id = this.dragElId;
30863             var s  = div.style;
30864
30865             s.position   = "absolute";
30866             s.visibility = "hidden";
30867             s.cursor     = "move";
30868             s.border     = "2px solid #aaa";
30869             s.zIndex     = 999;
30870
30871             // appendChild can blow up IE if invoked prior to the window load event
30872             // while rendering a table.  It is possible there are other scenarios
30873             // that would cause this to happen as well.
30874             body.insertBefore(div, body.firstChild);
30875         }
30876     },
30877
30878     /**
30879      * Initialization for the drag frame element.  Must be called in the
30880      * constructor of all subclasses
30881      * @method initFrame
30882      */
30883     initFrame: function() {
30884         this.createFrame();
30885     },
30886
30887     applyConfig: function() {
30888         Ext.dd.DDProxy.superclass.applyConfig.call(this);
30889
30890         this.resizeFrame = (this.config.resizeFrame !== false);
30891         this.centerFrame = (this.config.centerFrame);
30892         this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
30893     },
30894
30895     /**
30896      * Resizes the drag frame to the dimensions of the clicked object, positions
30897      * it over the object, and finally displays it
30898      * @method showFrame
30899      * @param {int} iPageX X click position
30900      * @param {int} iPageY Y click position
30901      * @private
30902      */
30903     showFrame: function(iPageX, iPageY) {
30904         var el = this.getEl();
30905         var dragEl = this.getDragEl();
30906         var s = dragEl.style;
30907
30908         this._resizeProxy();
30909
30910         if (this.centerFrame) {
30911             this.setDelta( Math.round(parseInt(s.width,  10)/2),
30912                            Math.round(parseInt(s.height, 10)/2) );
30913         }
30914
30915         this.setDragElPos(iPageX, iPageY);
30916
30917         Ext.fly(dragEl).show();
30918     },
30919
30920     /**
30921      * The proxy is automatically resized to the dimensions of the linked
30922      * element when a drag is initiated, unless resizeFrame is set to false
30923      * @method _resizeProxy
30924      * @private
30925      */
30926     _resizeProxy: function() {
30927         if (this.resizeFrame) {
30928             var el = this.getEl();
30929             Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
30930         }
30931     },
30932
30933     // overrides Ext.dd.DragDrop
30934     b4MouseDown: function(e) {
30935         var x = e.getPageX();
30936         var y = e.getPageY();
30937         this.autoOffset(x, y);
30938         this.setDragElPos(x, y);
30939     },
30940
30941     // overrides Ext.dd.DragDrop
30942     b4StartDrag: function(x, y) {
30943         // show the drag frame
30944         this.showFrame(x, y);
30945     },
30946
30947     // overrides Ext.dd.DragDrop
30948     b4EndDrag: function(e) {
30949         Ext.fly(this.getDragEl()).hide();
30950     },
30951
30952     // overrides Ext.dd.DragDrop
30953     // By default we try to move the element to the last location of the frame.
30954     // This is so that the default behavior mirrors that of Ext.dd.DD.
30955     endDrag: function(e) {
30956
30957         var lel = this.getEl();
30958         var del = this.getDragEl();
30959
30960         // Show the drag frame briefly so we can get its position
30961         del.style.visibility = "";
30962
30963         this.beforeMove();
30964         // Hide the linked element before the move to get around a Safari
30965         // rendering bug.
30966         lel.style.visibility = "hidden";
30967         Ext.dd.DDM.moveToEl(lel, del);
30968         del.style.visibility = "hidden";
30969         lel.style.visibility = "";
30970
30971         this.afterDrag();
30972     },
30973
30974     beforeMove : function(){
30975
30976     },
30977
30978     afterDrag : function(){
30979
30980     },
30981
30982     toString: function() {
30983         return ("DDProxy " + this.id);
30984     }
30985
30986 });
30987 /**
30988  * @class Ext.dd.DDTarget
30989  * A DragDrop implementation that does not move, but can be a drop
30990  * target.  You would get the same result by simply omitting implementation
30991  * for the event callbacks, but this way we reduce the processing cost of the
30992  * event listener and the callbacks.
30993  * @extends Ext.dd.DragDrop
30994  * @constructor
30995  * @param {String} id the id of the element that is a drop target
30996  * @param {String} sGroup the group of related DragDrop objects
30997  * @param {object} config an object containing configurable attributes
30998  *                 Valid properties for DDTarget in addition to those in
30999  *                 DragDrop:
31000  *                    none
31001  */
31002 Ext.dd.DDTarget = function(id, sGroup, config) {
31003     if (id) {
31004         this.initTarget(id, sGroup, config);
31005     }
31006 };
31007
31008 // Ext.dd.DDTarget.prototype = new Ext.dd.DragDrop();
31009 Ext.extend(Ext.dd.DDTarget, Ext.dd.DragDrop, {
31010     /**
31011      * @hide
31012      * Overridden and disabled. A DDTarget does not support being dragged.
31013      * @method
31014      */
31015     getDragEl: Ext.emptyFn,
31016     /**
31017      * @hide
31018      * Overridden and disabled. A DDTarget does not support being dragged.
31019      * @method
31020      */
31021     isValidHandleChild: Ext.emptyFn,
31022     /**
31023      * @hide
31024      * Overridden and disabled. A DDTarget does not support being dragged.
31025      * @method
31026      */
31027     startDrag: Ext.emptyFn,
31028     /**
31029      * @hide
31030      * Overridden and disabled. A DDTarget does not support being dragged.
31031      * @method
31032      */
31033     endDrag: Ext.emptyFn,
31034     /**
31035      * @hide
31036      * Overridden and disabled. A DDTarget does not support being dragged.
31037      * @method
31038      */
31039     onDrag: Ext.emptyFn,
31040     /**
31041      * @hide
31042      * Overridden and disabled. A DDTarget does not support being dragged.
31043      * @method
31044      */
31045     onDragDrop: Ext.emptyFn,
31046     /**
31047      * @hide
31048      * Overridden and disabled. A DDTarget does not support being dragged.
31049      * @method
31050      */
31051     onDragEnter: Ext.emptyFn,
31052     /**
31053      * @hide
31054      * Overridden and disabled. A DDTarget does not support being dragged.
31055      * @method
31056      */
31057     onDragOut: Ext.emptyFn,
31058     /**
31059      * @hide
31060      * Overridden and disabled. A DDTarget does not support being dragged.
31061      * @method
31062      */
31063     onDragOver: Ext.emptyFn,
31064     /**
31065      * @hide
31066      * Overridden and disabled. A DDTarget does not support being dragged.
31067      * @method
31068      */
31069     onInvalidDrop: Ext.emptyFn,
31070     /**
31071      * @hide
31072      * Overridden and disabled. A DDTarget does not support being dragged.
31073      * @method
31074      */
31075     onMouseDown: Ext.emptyFn,
31076     /**
31077      * @hide
31078      * Overridden and disabled. A DDTarget does not support being dragged.
31079      * @method
31080      */
31081     onMouseUp: Ext.emptyFn,
31082     /**
31083      * @hide
31084      * Overridden and disabled. A DDTarget does not support being dragged.
31085      * @method
31086      */
31087     setXConstraint: Ext.emptyFn,
31088     /**
31089      * @hide
31090      * Overridden and disabled. A DDTarget does not support being dragged.
31091      * @method
31092      */
31093     setYConstraint: Ext.emptyFn,
31094     /**
31095      * @hide
31096      * Overridden and disabled. A DDTarget does not support being dragged.
31097      * @method
31098      */
31099     resetConstraints: Ext.emptyFn,
31100     /**
31101      * @hide
31102      * Overridden and disabled. A DDTarget does not support being dragged.
31103      * @method
31104      */
31105     clearConstraints: Ext.emptyFn,
31106     /**
31107      * @hide
31108      * Overridden and disabled. A DDTarget does not support being dragged.
31109      * @method
31110      */
31111     clearTicks: Ext.emptyFn,
31112     /**
31113      * @hide
31114      * Overridden and disabled. A DDTarget does not support being dragged.
31115      * @method
31116      */
31117     setInitPosition: Ext.emptyFn,
31118     /**
31119      * @hide
31120      * Overridden and disabled. A DDTarget does not support being dragged.
31121      * @method
31122      */
31123     setDragElId: Ext.emptyFn,
31124     /**
31125      * @hide
31126      * Overridden and disabled. A DDTarget does not support being dragged.
31127      * @method
31128      */
31129     setHandleElId: Ext.emptyFn,
31130     /**
31131      * @hide
31132      * Overridden and disabled. A DDTarget does not support being dragged.
31133      * @method
31134      */
31135     setOuterHandleElId: Ext.emptyFn,
31136     /**
31137      * @hide
31138      * Overridden and disabled. A DDTarget does not support being dragged.
31139      * @method
31140      */
31141     addInvalidHandleClass: Ext.emptyFn,
31142     /**
31143      * @hide
31144      * Overridden and disabled. A DDTarget does not support being dragged.
31145      * @method
31146      */
31147     addInvalidHandleId: Ext.emptyFn,
31148     /**
31149      * @hide
31150      * Overridden and disabled. A DDTarget does not support being dragged.
31151      * @method
31152      */
31153     addInvalidHandleType: Ext.emptyFn,
31154     /**
31155      * @hide
31156      * Overridden and disabled. A DDTarget does not support being dragged.
31157      * @method
31158      */
31159     removeInvalidHandleClass: Ext.emptyFn,
31160     /**
31161      * @hide
31162      * Overridden and disabled. A DDTarget does not support being dragged.
31163      * @method
31164      */
31165     removeInvalidHandleId: Ext.emptyFn,
31166     /**
31167      * @hide
31168      * Overridden and disabled. A DDTarget does not support being dragged.
31169      * @method
31170      */
31171     removeInvalidHandleType: Ext.emptyFn,
31172
31173     toString: function() {
31174         return ("DDTarget " + this.id);
31175     }
31176 });/**
31177  * @class Ext.dd.DragTracker
31178  * @extends Ext.util.Observable
31179  * A DragTracker listens for drag events on an Element and fires events at the start and end of the drag,
31180  * as well as during the drag. This is useful for components such as {@link Ext.Slider}, where there is
31181  * an element that can be dragged around to change the Slider's value.
31182  * DragTracker provides a series of template methods that should be overridden to provide functionality
31183  * in response to detected drag operations. These are onBeforeStart, onStart, onDrag and onEnd.
31184  * See {@link Ext.Slider}'s initEvents function for an example implementation.
31185  */
31186 Ext.dd.DragTracker = Ext.extend(Ext.util.Observable,  {    
31187     /**
31188      * @cfg {Boolean} active
31189          * Defaults to <tt>false</tt>.
31190          */     
31191     active: false,
31192     /**
31193      * @cfg {Number} tolerance
31194          * Number of pixels the drag target must be moved before dragging is considered to have started. Defaults to <tt>5</tt>.
31195          */     
31196     tolerance: 5,
31197     /**
31198      * @cfg {Boolean/Number} autoStart
31199          * Defaults to <tt>false</tt>. Specify <tt>true</tt> to defer trigger start by 1000 ms.
31200          * Specify a Number for the number of milliseconds to defer trigger start.
31201          */     
31202     autoStart: false,
31203     
31204     constructor : function(config){
31205         Ext.apply(this, config);
31206             this.addEvents(
31207                 /**
31208                  * @event mousedown
31209                  * @param {Object} this
31210                  * @param {Object} e event object
31211                  */
31212                 'mousedown',
31213                 /**
31214                  * @event mouseup
31215                  * @param {Object} this
31216                  * @param {Object} e event object
31217                  */
31218                 'mouseup',
31219                 /**
31220                  * @event mousemove
31221                  * @param {Object} this
31222                  * @param {Object} e event object
31223                  */
31224                 'mousemove',
31225                 /**
31226                  * @event dragstart
31227                  * @param {Object} this
31228                  * @param {Object} startXY the page coordinates of the event
31229                  */
31230                 'dragstart',
31231                 /**
31232                  * @event dragend
31233                  * @param {Object} this
31234                  * @param {Object} e event object
31235                  */
31236                 'dragend',
31237                 /**
31238                  * @event drag
31239                  * @param {Object} this
31240                  * @param {Object} e event object
31241                  */
31242                 'drag'
31243             );
31244         
31245             this.dragRegion = new Ext.lib.Region(0,0,0,0);
31246         
31247             if(this.el){
31248                 this.initEl(this.el);
31249             }
31250         Ext.dd.DragTracker.superclass.constructor.call(this, config);
31251     },
31252
31253     initEl: function(el){
31254         this.el = Ext.get(el);
31255         el.on('mousedown', this.onMouseDown, this,
31256                 this.delegate ? {delegate: this.delegate} : undefined);
31257     },
31258
31259     destroy : function(){
31260         this.el.un('mousedown', this.onMouseDown, this);
31261     },
31262
31263     onMouseDown: function(e, target){
31264         if(this.fireEvent('mousedown', this, e) !== false && this.onBeforeStart(e) !== false){
31265             this.startXY = this.lastXY = e.getXY();
31266             this.dragTarget = this.delegate ? target : this.el.dom;
31267             if(this.preventDefault !== false){
31268                 e.preventDefault();
31269             }
31270             var doc = Ext.getDoc();
31271             doc.on('mouseup', this.onMouseUp, this);
31272             doc.on('mousemove', this.onMouseMove, this);
31273             doc.on('selectstart', this.stopSelect, this);
31274             if(this.autoStart){
31275                 this.timer = this.triggerStart.defer(this.autoStart === true ? 1000 : this.autoStart, this);
31276             }
31277         }
31278     },
31279
31280     onMouseMove: function(e, target){
31281         // HACK: IE hack to see if button was released outside of window. */
31282         if(this.active && Ext.isIE && !e.browserEvent.button){
31283             e.preventDefault();
31284             this.onMouseUp(e);
31285             return;
31286         }
31287
31288         e.preventDefault();
31289         var xy = e.getXY(), s = this.startXY;
31290         this.lastXY = xy;
31291         if(!this.active){
31292             if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){
31293                 this.triggerStart();
31294             }else{
31295                 return;
31296             }
31297         }
31298         this.fireEvent('mousemove', this, e);
31299         this.onDrag(e);
31300         this.fireEvent('drag', this, e);
31301     },
31302
31303     onMouseUp: function(e) {
31304         var doc = Ext.getDoc();
31305         doc.un('mousemove', this.onMouseMove, this);
31306         doc.un('mouseup', this.onMouseUp, this);
31307         doc.un('selectstart', this.stopSelect, this);
31308         e.preventDefault();
31309         this.clearStart();
31310         var wasActive = this.active;
31311         this.active = false;
31312         delete this.elRegion;
31313         this.fireEvent('mouseup', this, e);
31314         if(wasActive){
31315             this.onEnd(e);
31316             this.fireEvent('dragend', this, e);
31317         }
31318     },
31319
31320     triggerStart: function(isTimer) {
31321         this.clearStart();
31322         this.active = true;
31323         this.onStart(this.startXY);
31324         this.fireEvent('dragstart', this, this.startXY);
31325     },
31326
31327     clearStart : function() {
31328         if(this.timer){
31329             clearTimeout(this.timer);
31330             delete this.timer;
31331         }
31332     },
31333
31334     stopSelect : function(e) {
31335         e.stopEvent();
31336         return false;
31337     },
31338     
31339     /**
31340      * Template method which should be overridden by each DragTracker instance. Called when the user first clicks and
31341      * holds the mouse button down. Return false to disallow the drag
31342      * @param {Ext.EventObject} e The event object
31343      */
31344     onBeforeStart : function(e) {
31345
31346     },
31347
31348     /**
31349      * Template method which should be overridden by each DragTracker instance. Called when a drag operation starts
31350      * (e.g. the user has moved the tracked element beyond the specified tolerance)
31351      * @param {Array} xy x and y co-ordinates of the original location of the tracked element
31352      */
31353     onStart : function(xy) {
31354
31355     },
31356
31357     /**
31358      * Template method which should be overridden by each DragTracker instance. Called whenever a drag has been detected.
31359      * @param {Ext.EventObject} e The event object
31360      */
31361     onDrag : function(e) {
31362
31363     },
31364
31365     /**
31366      * Template method which should be overridden by each DragTracker instance. Called when a drag operation has been completed
31367      * (e.g. the user clicked and held the mouse down, dragged the element and then released the mouse button)
31368      * @param {Ext.EventObject} e The event object
31369      */
31370     onEnd : function(e) {
31371
31372     },
31373
31374     /**
31375      * Returns the drag target
31376      * @return {Ext.Element} The element currently being tracked
31377      */
31378     getDragTarget : function(){
31379         return this.dragTarget;
31380     },
31381
31382     getDragCt : function(){
31383         return this.el;
31384     },
31385
31386     getXY : function(constrain){
31387         return constrain ?
31388                this.constrainModes[constrain].call(this, this.lastXY) : this.lastXY;
31389     },
31390
31391     getOffset : function(constrain){
31392         var xy = this.getXY(constrain);
31393         var s = this.startXY;
31394         return [s[0]-xy[0], s[1]-xy[1]];
31395     },
31396
31397     constrainModes: {
31398         'point' : function(xy){
31399
31400             if(!this.elRegion){
31401                 this.elRegion = this.getDragCt().getRegion();
31402             }
31403
31404             var dr = this.dragRegion;
31405
31406             dr.left = xy[0];
31407             dr.top = xy[1];
31408             dr.right = xy[0];
31409             dr.bottom = xy[1];
31410
31411             dr.constrainTo(this.elRegion);
31412
31413             return [dr.left, dr.top];
31414         }
31415     }
31416 });/**
31417  * @class Ext.dd.ScrollManager
31418  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>
31419  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,
31420  * but you can also override most of the configs per scroll container by adding a 
31421  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},
31422  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:
31423  * <pre><code>
31424 var el = Ext.get('scroll-ct');
31425 el.ddScrollConfig = {
31426     vthresh: 50,
31427     hthresh: -1,
31428     frequency: 100,
31429     increment: 200
31430 };
31431 Ext.dd.ScrollManager.register(el);
31432 </code></pre>
31433  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
31434  * @singleton
31435  */
31436 Ext.dd.ScrollManager = function(){
31437     var ddm = Ext.dd.DragDropMgr;
31438     var els = {};
31439     var dragEl = null;
31440     var proc = {};
31441     
31442     var onStop = function(e){
31443         dragEl = null;
31444         clearProc();
31445     };
31446     
31447     var triggerRefresh = function(){
31448         if(ddm.dragCurrent){
31449              ddm.refreshCache(ddm.dragCurrent.groups);
31450         }
31451     };
31452     
31453     var doScroll = function(){
31454         if(ddm.dragCurrent){
31455             var dds = Ext.dd.ScrollManager;
31456             var inc = proc.el.ddScrollConfig ?
31457                       proc.el.ddScrollConfig.increment : dds.increment;
31458             if(!dds.animate){
31459                 if(proc.el.scroll(proc.dir, inc)){
31460                     triggerRefresh();
31461                 }
31462             }else{
31463                 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);
31464             }
31465         }
31466     };
31467     
31468     var clearProc = function(){
31469         if(proc.id){
31470             clearInterval(proc.id);
31471         }
31472         proc.id = 0;
31473         proc.el = null;
31474         proc.dir = "";
31475     };
31476     
31477     var startProc = function(el, dir){
31478         clearProc();
31479         proc.el = el;
31480         proc.dir = dir;
31481         var freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ? 
31482                 el.ddScrollConfig.frequency : Ext.dd.ScrollManager.frequency;
31483         proc.id = setInterval(doScroll, freq);
31484     };
31485     
31486     var onFire = function(e, isDrop){
31487         if(isDrop || !ddm.dragCurrent){ return; }
31488         var dds = Ext.dd.ScrollManager;
31489         if(!dragEl || dragEl != ddm.dragCurrent){
31490             dragEl = ddm.dragCurrent;
31491             // refresh regions on drag start
31492             dds.refreshCache();
31493         }
31494         
31495         var xy = Ext.lib.Event.getXY(e);
31496         var pt = new Ext.lib.Point(xy[0], xy[1]);
31497         for(var id in els){
31498             var el = els[id], r = el._region;
31499             var c = el.ddScrollConfig ? el.ddScrollConfig : dds;
31500             if(r && r.contains(pt) && el.isScrollable()){
31501                 if(r.bottom - pt.y <= c.vthresh){
31502                     if(proc.el != el){
31503                         startProc(el, "down");
31504                     }
31505                     return;
31506                 }else if(r.right - pt.x <= c.hthresh){
31507                     if(proc.el != el){
31508                         startProc(el, "left");
31509                     }
31510                     return;
31511                 }else if(pt.y - r.top <= c.vthresh){
31512                     if(proc.el != el){
31513                         startProc(el, "up");
31514                     }
31515                     return;
31516                 }else if(pt.x - r.left <= c.hthresh){
31517                     if(proc.el != el){
31518                         startProc(el, "right");
31519                     }
31520                     return;
31521                 }
31522             }
31523         }
31524         clearProc();
31525     };
31526     
31527     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
31528     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
31529     
31530     return {
31531         /**
31532          * Registers new overflow element(s) to auto scroll
31533          * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either
31534          */
31535         register : function(el){
31536             if(Ext.isArray(el)){
31537                 for(var i = 0, len = el.length; i < len; i++) {
31538                         this.register(el[i]);
31539                 }
31540             }else{
31541                 el = Ext.get(el);
31542                 els[el.id] = el;
31543             }
31544         },
31545         
31546         /**
31547          * Unregisters overflow element(s) so they are no longer scrolled
31548          * @param {Mixed/Array} el The id of or the element to be removed or an array of either
31549          */
31550         unregister : function(el){
31551             if(Ext.isArray(el)){
31552                 for(var i = 0, len = el.length; i < len; i++) {
31553                         this.unregister(el[i]);
31554                 }
31555             }else{
31556                 el = Ext.get(el);
31557                 delete els[el.id];
31558             }
31559         },
31560         
31561         /**
31562          * The number of pixels from the top or bottom edge of a container the pointer needs to be to
31563          * trigger scrolling (defaults to 25)
31564          * @type Number
31565          */
31566         vthresh : 25,
31567         /**
31568          * The number of pixels from the right or left edge of a container the pointer needs to be to
31569          * trigger scrolling (defaults to 25)
31570          * @type Number
31571          */
31572         hthresh : 25,
31573
31574         /**
31575          * The number of pixels to scroll in each scroll increment (defaults to 50)
31576          * @type Number
31577          */
31578         increment : 100,
31579         
31580         /**
31581          * The frequency of scrolls in milliseconds (defaults to 500)
31582          * @type Number
31583          */
31584         frequency : 500,
31585         
31586         /**
31587          * True to animate the scroll (defaults to true)
31588          * @type Boolean
31589          */
31590         animate: true,
31591         
31592         /**
31593          * The animation duration in seconds - 
31594          * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)
31595          * @type Number
31596          */
31597         animDuration: .4,
31598         
31599         /**
31600          * Manually trigger a cache refresh.
31601          */
31602         refreshCache : function(){
31603             for(var id in els){
31604                 if(typeof els[id] == 'object'){ // for people extending the object prototype
31605                     els[id]._region = els[id].getRegion();
31606                 }
31607             }
31608         }
31609     };
31610 }();/**
31611  * @class Ext.dd.Registry
31612  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
31613  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
31614  * @singleton
31615  */
31616 Ext.dd.Registry = function(){
31617     var elements = {}; 
31618     var handles = {}; 
31619     var autoIdSeed = 0;
31620
31621     var getId = function(el, autogen){
31622         if(typeof el == "string"){
31623             return el;
31624         }
31625         var id = el.id;
31626         if(!id && autogen !== false){
31627             id = "extdd-" + (++autoIdSeed);
31628             el.id = id;
31629         }
31630         return id;
31631     };
31632     
31633     return {
31634     /**
31635      * Resgister a drag drop element
31636      * @param {String/HTMLElement} element The id or DOM node to register
31637      * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved
31638      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
31639      * knows how to interpret, plus there are some specific properties known to the Registry that should be
31640      * populated in the data object (if applicable):
31641      * <pre>
31642 Value      Description<br />
31643 ---------  ------------------------------------------<br />
31644 handles    Array of DOM nodes that trigger dragging<br />
31645            for the element being registered<br />
31646 isHandle   True if the element passed in triggers<br />
31647            dragging itself, else false
31648 </pre>
31649      */
31650         register : function(el, data){
31651             data = data || {};
31652             if(typeof el == "string"){
31653                 el = document.getElementById(el);
31654             }
31655             data.ddel = el;
31656             elements[getId(el)] = data;
31657             if(data.isHandle !== false){
31658                 handles[data.ddel.id] = data;
31659             }
31660             if(data.handles){
31661                 var hs = data.handles;
31662                 for(var i = 0, len = hs.length; i < len; i++){
31663                         handles[getId(hs[i])] = data;
31664                 }
31665             }
31666         },
31667
31668     /**
31669      * Unregister a drag drop element
31670      * @param {String/HTMLElement} element The id or DOM node to unregister
31671      */
31672         unregister : function(el){
31673             var id = getId(el, false);
31674             var data = elements[id];
31675             if(data){
31676                 delete elements[id];
31677                 if(data.handles){
31678                     var hs = data.handles;
31679                     for(var i = 0, len = hs.length; i < len; i++){
31680                         delete handles[getId(hs[i], false)];
31681                     }
31682                 }
31683             }
31684         },
31685
31686     /**
31687      * Returns the handle registered for a DOM Node by id
31688      * @param {String/HTMLElement} id The DOM node or id to look up
31689      * @return {Object} handle The custom handle data
31690      */
31691         getHandle : function(id){
31692             if(typeof id != "string"){ // must be element?
31693                 id = id.id;
31694             }
31695             return handles[id];
31696         },
31697
31698     /**
31699      * Returns the handle that is registered for the DOM node that is the target of the event
31700      * @param {Event} e The event
31701      * @return {Object} handle The custom handle data
31702      */
31703         getHandleFromEvent : function(e){
31704             var t = Ext.lib.Event.getTarget(e);
31705             return t ? handles[t.id] : null;
31706         },
31707
31708     /**
31709      * Returns a custom data object that is registered for a DOM node by id
31710      * @param {String/HTMLElement} id The DOM node or id to look up
31711      * @return {Object} data The custom data
31712      */
31713         getTarget : function(id){
31714             if(typeof id != "string"){ // must be element?
31715                 id = id.id;
31716             }
31717             return elements[id];
31718         },
31719
31720     /**
31721      * Returns a custom data object that is registered for the DOM node that is the target of the event
31722      * @param {Event} e The event
31723      * @return {Object} data The custom data
31724      */
31725         getTargetFromEvent : function(e){
31726             var t = Ext.lib.Event.getTarget(e);
31727             return t ? elements[t.id] || handles[t.id] : null;
31728         }
31729     };
31730 }();/**
31731  * @class Ext.dd.StatusProxy
31732  * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the
31733  * default drag proxy used by all Ext.dd components.
31734  * @constructor
31735  * @param {Object} config
31736  */
31737 Ext.dd.StatusProxy = function(config){
31738     Ext.apply(this, config);
31739     this.id = this.id || Ext.id();
31740     this.el = new Ext.Layer({
31741         dh: {
31742             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
31743                 {tag: "div", cls: "x-dd-drop-icon"},
31744                 {tag: "div", cls: "x-dd-drag-ghost"}
31745             ]
31746         }, 
31747         shadow: !config || config.shadow !== false
31748     });
31749     this.ghost = Ext.get(this.el.dom.childNodes[1]);
31750     this.dropStatus = this.dropNotAllowed;
31751 };
31752
31753 Ext.dd.StatusProxy.prototype = {
31754     /**
31755      * @cfg {String} dropAllowed
31756      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
31757      */
31758     dropAllowed : "x-dd-drop-ok",
31759     /**
31760      * @cfg {String} dropNotAllowed
31761      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
31762      */
31763     dropNotAllowed : "x-dd-drop-nodrop",
31764
31765     /**
31766      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
31767      * over the current target element.
31768      * @param {String} cssClass The css class for the new drop status indicator image
31769      */
31770     setStatus : function(cssClass){
31771         cssClass = cssClass || this.dropNotAllowed;
31772         if(this.dropStatus != cssClass){
31773             this.el.replaceClass(this.dropStatus, cssClass);
31774             this.dropStatus = cssClass;
31775         }
31776     },
31777
31778     /**
31779      * Resets the status indicator to the default dropNotAllowed value
31780      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
31781      */
31782     reset : function(clearGhost){
31783         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
31784         this.dropStatus = this.dropNotAllowed;
31785         if(clearGhost){
31786             this.ghost.update("");
31787         }
31788     },
31789
31790     /**
31791      * Updates the contents of the ghost element
31792      * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a
31793      * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).
31794      */
31795     update : function(html){
31796         if(typeof html == "string"){
31797             this.ghost.update(html);
31798         }else{
31799             this.ghost.update("");
31800             html.style.margin = "0";
31801             this.ghost.dom.appendChild(html);
31802         }
31803         var el = this.ghost.dom.firstChild; 
31804         if(el){
31805             Ext.fly(el).setStyle('float', 'none');
31806         }
31807     },
31808
31809     /**
31810      * Returns the underlying proxy {@link Ext.Layer}
31811      * @return {Ext.Layer} el
31812     */
31813     getEl : function(){
31814         return this.el;
31815     },
31816
31817     /**
31818      * Returns the ghost element
31819      * @return {Ext.Element} el
31820      */
31821     getGhost : function(){
31822         return this.ghost;
31823     },
31824
31825     /**
31826      * Hides the proxy
31827      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
31828      */
31829     hide : function(clear){
31830         this.el.hide();
31831         if(clear){
31832             this.reset(true);
31833         }
31834     },
31835
31836     /**
31837      * Stops the repair animation if it's currently running
31838      */
31839     stop : function(){
31840         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
31841             this.anim.stop();
31842         }
31843     },
31844
31845     /**
31846      * Displays this proxy
31847      */
31848     show : function(){
31849         this.el.show();
31850     },
31851
31852     /**
31853      * Force the Layer to sync its shadow and shim positions to the element
31854      */
31855     sync : function(){
31856         this.el.sync();
31857     },
31858
31859     /**
31860      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
31861      * invalid drop operation by the item being dragged.
31862      * @param {Array} xy The XY position of the element ([x, y])
31863      * @param {Function} callback The function to call after the repair is complete.
31864      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
31865      */
31866     repair : function(xy, callback, scope){
31867         this.callback = callback;
31868         this.scope = scope;
31869         if(xy && this.animRepair !== false){
31870             this.el.addClass("x-dd-drag-repair");
31871             this.el.hideUnders(true);
31872             this.anim = this.el.shift({
31873                 duration: this.repairDuration || .5,
31874                 easing: 'easeOut',
31875                 xy: xy,
31876                 stopFx: true,
31877                 callback: this.afterRepair,
31878                 scope: this
31879             });
31880         }else{
31881             this.afterRepair();
31882         }
31883     },
31884
31885     // private
31886     afterRepair : function(){
31887         this.hide(true);
31888         if(typeof this.callback == "function"){
31889             this.callback.call(this.scope || this);
31890         }
31891         this.callback = null;
31892         this.scope = null;
31893     },
31894     
31895     destroy: function(){
31896         Ext.destroy(this.ghost, this.el);    
31897     }
31898 };/**
31899  * @class Ext.dd.DragSource
31900  * @extends Ext.dd.DDProxy
31901  * A simple class that provides the basic implementation needed to make any element draggable.
31902  * @constructor
31903  * @param {Mixed} el The container element
31904  * @param {Object} config
31905  */
31906 Ext.dd.DragSource = function(el, config){
31907     this.el = Ext.get(el);
31908     if(!this.dragData){
31909         this.dragData = {};
31910     }
31911     
31912     Ext.apply(this, config);
31913     
31914     if(!this.proxy){
31915         this.proxy = new Ext.dd.StatusProxy();
31916     }
31917     Ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
31918           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
31919     
31920     this.dragging = false;
31921 };
31922
31923 Ext.extend(Ext.dd.DragSource, Ext.dd.DDProxy, {
31924     /**
31925      * @cfg {String} ddGroup
31926      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
31927      * interact with other drag drop objects in the same group (defaults to undefined).
31928      */
31929     /**
31930      * @cfg {String} dropAllowed
31931      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
31932      */
31933     dropAllowed : "x-dd-drop-ok",
31934     /**
31935      * @cfg {String} dropNotAllowed
31936      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
31937      */
31938     dropNotAllowed : "x-dd-drop-nodrop",
31939
31940     /**
31941      * Returns the data object associated with this drag source
31942      * @return {Object} data An object containing arbitrary data
31943      */
31944     getDragData : function(e){
31945         return this.dragData;
31946     },
31947
31948     // private
31949     onDragEnter : function(e, id){
31950         var target = Ext.dd.DragDropMgr.getDDById(id);
31951         this.cachedTarget = target;
31952         if(this.beforeDragEnter(target, e, id) !== false){
31953             if(target.isNotifyTarget){
31954                 var status = target.notifyEnter(this, e, this.dragData);
31955                 this.proxy.setStatus(status);
31956             }else{
31957                 this.proxy.setStatus(this.dropAllowed);
31958             }
31959             
31960             if(this.afterDragEnter){
31961                 /**
31962                  * An empty function by default, but provided so that you can perform a custom action
31963                  * when the dragged item enters the drop target by providing an implementation.
31964                  * @param {Ext.dd.DragDrop} target The drop target
31965                  * @param {Event} e The event object
31966                  * @param {String} id The id of the dragged element
31967                  * @method afterDragEnter
31968                  */
31969                 this.afterDragEnter(target, e, id);
31970             }
31971         }
31972     },
31973
31974     /**
31975      * An empty function by default, but provided so that you can perform a custom action
31976      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
31977      * @param {Ext.dd.DragDrop} target The drop target
31978      * @param {Event} e The event object
31979      * @param {String} id The id of the dragged element
31980      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
31981      */
31982     beforeDragEnter : function(target, e, id){
31983         return true;
31984     },
31985
31986     // private
31987     alignElWithMouse: function() {
31988         Ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
31989         this.proxy.sync();
31990     },
31991
31992     // private
31993     onDragOver : function(e, id){
31994         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
31995         if(this.beforeDragOver(target, e, id) !== false){
31996             if(target.isNotifyTarget){
31997                 var status = target.notifyOver(this, e, this.dragData);
31998                 this.proxy.setStatus(status);
31999             }
32000
32001             if(this.afterDragOver){
32002                 /**
32003                  * An empty function by default, but provided so that you can perform a custom action
32004                  * while the dragged item is over the drop target by providing an implementation.
32005                  * @param {Ext.dd.DragDrop} target The drop target
32006                  * @param {Event} e The event object
32007                  * @param {String} id The id of the dragged element
32008                  * @method afterDragOver
32009                  */
32010                 this.afterDragOver(target, e, id);
32011             }
32012         }
32013     },
32014
32015     /**
32016      * An empty function by default, but provided so that you can perform a custom action
32017      * while the dragged item is over the drop target and optionally cancel the onDragOver.
32018      * @param {Ext.dd.DragDrop} target The drop target
32019      * @param {Event} e The event object
32020      * @param {String} id The id of the dragged element
32021      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32022      */
32023     beforeDragOver : function(target, e, id){
32024         return true;
32025     },
32026
32027     // private
32028     onDragOut : function(e, id){
32029         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
32030         if(this.beforeDragOut(target, e, id) !== false){
32031             if(target.isNotifyTarget){
32032                 target.notifyOut(this, e, this.dragData);
32033             }
32034             this.proxy.reset();
32035             if(this.afterDragOut){
32036                 /**
32037                  * An empty function by default, but provided so that you can perform a custom action
32038                  * after the dragged item is dragged out of the target without dropping.
32039                  * @param {Ext.dd.DragDrop} target The drop target
32040                  * @param {Event} e The event object
32041                  * @param {String} id The id of the dragged element
32042                  * @method afterDragOut
32043                  */
32044                 this.afterDragOut(target, e, id);
32045             }
32046         }
32047         this.cachedTarget = null;
32048     },
32049
32050     /**
32051      * An empty function by default, but provided so that you can perform a custom action before the dragged
32052      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
32053      * @param {Ext.dd.DragDrop} target The drop target
32054      * @param {Event} e The event object
32055      * @param {String} id The id of the dragged element
32056      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32057      */
32058     beforeDragOut : function(target, e, id){
32059         return true;
32060     },
32061     
32062     // private
32063     onDragDrop : function(e, id){
32064         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
32065         if(this.beforeDragDrop(target, e, id) !== false){
32066             if(target.isNotifyTarget){
32067                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
32068                     this.onValidDrop(target, e, id);
32069                 }else{
32070                     this.onInvalidDrop(target, e, id);
32071                 }
32072             }else{
32073                 this.onValidDrop(target, e, id);
32074             }
32075             
32076             if(this.afterDragDrop){
32077                 /**
32078                  * An empty function by default, but provided so that you can perform a custom action
32079                  * after a valid drag drop has occurred by providing an implementation.
32080                  * @param {Ext.dd.DragDrop} target The drop target
32081                  * @param {Event} e The event object
32082                  * @param {String} id The id of the dropped element
32083                  * @method afterDragDrop
32084                  */
32085                 this.afterDragDrop(target, e, id);
32086             }
32087         }
32088         delete this.cachedTarget;
32089     },
32090
32091     /**
32092      * An empty function by default, but provided so that you can perform a custom action before the dragged
32093      * item is dropped onto the target and optionally cancel the onDragDrop.
32094      * @param {Ext.dd.DragDrop} target The drop target
32095      * @param {Event} e The event object
32096      * @param {String} id The id of the dragged element
32097      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
32098      */
32099     beforeDragDrop : function(target, e, id){
32100         return true;
32101     },
32102
32103     // private
32104     onValidDrop : function(target, e, id){
32105         this.hideProxy();
32106         if(this.afterValidDrop){
32107             /**
32108              * An empty function by default, but provided so that you can perform a custom action
32109              * after a valid drop has occurred by providing an implementation.
32110              * @param {Object} target The target DD 
32111              * @param {Event} e The event object
32112              * @param {String} id The id of the dropped element
32113              * @method afterInvalidDrop
32114              */
32115             this.afterValidDrop(target, e, id);
32116         }
32117     },
32118
32119     // private
32120     getRepairXY : function(e, data){
32121         return this.el.getXY();  
32122     },
32123
32124     // private
32125     onInvalidDrop : function(target, e, id){
32126         this.beforeInvalidDrop(target, e, id);
32127         if(this.cachedTarget){
32128             if(this.cachedTarget.isNotifyTarget){
32129                 this.cachedTarget.notifyOut(this, e, this.dragData);
32130             }
32131             this.cacheTarget = null;
32132         }
32133         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
32134
32135         if(this.afterInvalidDrop){
32136             /**
32137              * An empty function by default, but provided so that you can perform a custom action
32138              * after an invalid drop has occurred by providing an implementation.
32139              * @param {Event} e The event object
32140              * @param {String} id The id of the dropped element
32141              * @method afterInvalidDrop
32142              */
32143             this.afterInvalidDrop(e, id);
32144         }
32145     },
32146
32147     // private
32148     afterRepair : function(){
32149         if(Ext.enableFx){
32150             this.el.highlight(this.hlColor || "c3daf9");
32151         }
32152         this.dragging = false;
32153     },
32154
32155     /**
32156      * An empty function by default, but provided so that you can perform a custom action after an invalid
32157      * drop has occurred.
32158      * @param {Ext.dd.DragDrop} target The drop target
32159      * @param {Event} e The event object
32160      * @param {String} id The id of the dragged element
32161      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
32162      */
32163     beforeInvalidDrop : function(target, e, id){
32164         return true;
32165     },
32166
32167     // private
32168     handleMouseDown : function(e){
32169         if(this.dragging) {
32170             return;
32171         }
32172         var data = this.getDragData(e);
32173         if(data && this.onBeforeDrag(data, e) !== false){
32174             this.dragData = data;
32175             this.proxy.stop();
32176             Ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
32177         } 
32178     },
32179
32180     /**
32181      * An empty function by default, but provided so that you can perform a custom action before the initial
32182      * drag event begins and optionally cancel it.
32183      * @param {Object} data An object containing arbitrary data to be shared with drop targets
32184      * @param {Event} e The event object
32185      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32186      */
32187     onBeforeDrag : function(data, e){
32188         return true;
32189     },
32190
32191     /**
32192      * An empty function by default, but provided so that you can perform a custom action once the initial
32193      * drag event has begun.  The drag cannot be canceled from this function.
32194      * @param {Number} x The x position of the click on the dragged object
32195      * @param {Number} y The y position of the click on the dragged object
32196      */
32197     onStartDrag : Ext.emptyFn,
32198
32199     // private override
32200     startDrag : function(x, y){
32201         this.proxy.reset();
32202         this.dragging = true;
32203         this.proxy.update("");
32204         this.onInitDrag(x, y);
32205         this.proxy.show();
32206     },
32207
32208     // private
32209     onInitDrag : function(x, y){
32210         var clone = this.el.dom.cloneNode(true);
32211         clone.id = Ext.id(); // prevent duplicate ids
32212         this.proxy.update(clone);
32213         this.onStartDrag(x, y);
32214         return true;
32215     },
32216
32217     /**
32218      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}
32219      * @return {Ext.dd.StatusProxy} proxy The StatusProxy
32220      */
32221     getProxy : function(){
32222         return this.proxy;  
32223     },
32224
32225     /**
32226      * Hides the drag source's {@link Ext.dd.StatusProxy}
32227      */
32228     hideProxy : function(){
32229         this.proxy.hide();  
32230         this.proxy.reset(true);
32231         this.dragging = false;
32232     },
32233
32234     // private
32235     triggerCacheRefresh : function(){
32236         Ext.dd.DDM.refreshCache(this.groups);
32237     },
32238
32239     // private - override to prevent hiding
32240     b4EndDrag: function(e) {
32241     },
32242
32243     // private - override to prevent moving
32244     endDrag : function(e){
32245         this.onEndDrag(this.dragData, e);
32246     },
32247
32248     // private
32249     onEndDrag : function(data, e){
32250     },
32251     
32252     // private - pin to cursor
32253     autoOffset : function(x, y) {
32254         this.setDelta(-12, -20);
32255     },
32256     
32257     destroy: function(){
32258         Ext.dd.DragSource.superclass.destroy.call(this);
32259         Ext.destroy(this.proxy);
32260     }
32261 });/**
32262  * @class Ext.dd.DropTarget
32263  * @extends Ext.dd.DDTarget
32264  * A simple class that provides the basic implementation needed to make any element a drop target that can have
32265  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
32266  * @constructor
32267  * @param {Mixed} el The container element
32268  * @param {Object} config
32269  */
32270 Ext.dd.DropTarget = function(el, config){
32271     this.el = Ext.get(el);
32272     
32273     Ext.apply(this, config);
32274     
32275     if(this.containerScroll){
32276         Ext.dd.ScrollManager.register(this.el);
32277     }
32278     
32279     Ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
32280           {isTarget: true});
32281
32282 };
32283
32284 Ext.extend(Ext.dd.DropTarget, Ext.dd.DDTarget, {
32285     /**
32286      * @cfg {String} ddGroup
32287      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
32288      * interact with other drag drop objects in the same group (defaults to undefined).
32289      */
32290     /**
32291      * @cfg {String} overClass
32292      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
32293      */
32294     /**
32295      * @cfg {String} dropAllowed
32296      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
32297      */
32298     dropAllowed : "x-dd-drop-ok",
32299     /**
32300      * @cfg {String} dropNotAllowed
32301      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
32302      */
32303     dropNotAllowed : "x-dd-drop-nodrop",
32304
32305     // private
32306     isTarget : true,
32307
32308     // private
32309     isNotifyTarget : true,
32310
32311     /**
32312      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the
32313      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
32314      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
32315      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32316      * @param {Event} e The event
32317      * @param {Object} data An object containing arbitrary data supplied by the drag source
32318      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32319      * underlying {@link Ext.dd.StatusProxy} can be updated
32320      */
32321     notifyEnter : function(dd, e, data){
32322         if(this.overClass){
32323             this.el.addClass(this.overClass);
32324         }
32325         return this.dropAllowed;
32326     },
32327
32328     /**
32329      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.
32330      * This method will be called on every mouse movement while the drag source is over the drop target.
32331      * This default implementation simply returns the dropAllowed config value.
32332      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32333      * @param {Event} e The event
32334      * @param {Object} data An object containing arbitrary data supplied by the drag source
32335      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32336      * underlying {@link Ext.dd.StatusProxy} can be updated
32337      */
32338     notifyOver : function(dd, e, data){
32339         return this.dropAllowed;
32340     },
32341
32342     /**
32343      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged
32344      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
32345      * overClass (if any) from the drop element.
32346      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32347      * @param {Event} e The event
32348      * @param {Object} data An object containing arbitrary data supplied by the drag source
32349      */
32350     notifyOut : function(dd, e, data){
32351         if(this.overClass){
32352             this.el.removeClass(this.overClass);
32353         }
32354     },
32355
32356     /**
32357      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has
32358      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
32359      * implementation that does something to process the drop event and returns true so that the drag source's
32360      * repair action does not run.
32361      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32362      * @param {Event} e The event
32363      * @param {Object} data An object containing arbitrary data supplied by the drag source
32364      * @return {Boolean} True if the drop was valid, else false
32365      */
32366     notifyDrop : function(dd, e, data){
32367         return false;
32368     }
32369 });/**
32370  * @class Ext.dd.DragZone
32371  * @extends Ext.dd.DragSource
32372  * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>
32373  * <p>This class does not move the drag target nodes, but a proxy element which may contain
32374  * any DOM structure you wish. The DOM element to show in the proxy is provided by either a
32375  * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>
32376  * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some
32377  * application object (For example nodes in a {@link Ext.DataView DataView}) then use of this class
32378  * is the most efficient way to "activate" those nodes.</p>
32379  * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.
32380  * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure
32381  * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed
32382  * mouse event to see if it has taken place within an element, or class of elements. This is easily done
32383  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
32384  * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following
32385  * technique. Knowledge of the use of the DataView is required:</p><pre><code>
32386 myDataView.on('render', function(v) {
32387     myDataView.dragZone = new Ext.dd.DragZone(v.getEl(), {
32388
32389 //      On receipt of a mousedown event, see if it is within a DataView node.
32390 //      Return a drag data object if so.
32391         getDragData: function(e) {
32392
32393 //          Use the DataView's own itemSelector (a mandatory property) to
32394 //          test if the mousedown is within one of the DataView's nodes.
32395             var sourceEl = e.getTarget(v.itemSelector, 10);
32396
32397 //          If the mousedown is within a DataView node, clone the node to produce
32398 //          a ddel element for use by the drag proxy. Also add application data
32399 //          to the returned data object.
32400             if (sourceEl) {
32401                 d = sourceEl.cloneNode(true);
32402                 d.id = Ext.id();
32403                 return {
32404                     ddel: d,
32405                     sourceEl: sourceEl,
32406                     repairXY: Ext.fly(sourceEl).getXY(),
32407                     sourceStore: v.store,
32408                     draggedRecord: v.{@link Ext.DataView#getRecord getRecord}(sourceEl)
32409                 }
32410             }
32411         },
32412
32413 //      Provide coordinates for the proxy to slide back to on failed drag.
32414 //      This is the original XY coordinates of the draggable element captured
32415 //      in the getDragData method.
32416         getRepairXY: function() {
32417             return this.dragData.repairXY;
32418         }
32419     });
32420 });</code></pre>
32421  * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which
32422  * cooperates with this DragZone.
32423  * @constructor
32424  * @param {Mixed} el The container element
32425  * @param {Object} config
32426  */
32427 Ext.dd.DragZone = function(el, config){
32428     Ext.dd.DragZone.superclass.constructor.call(this, el, config);
32429     if(this.containerScroll){
32430         Ext.dd.ScrollManager.register(this.el);
32431     }
32432 };
32433
32434 Ext.extend(Ext.dd.DragZone, Ext.dd.DragSource, {
32435     /**
32436      * This property contains the data representing the dragged object. This data is set up by the implementation
32437      * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain
32438      * any other data according to the application's needs.
32439      * @type Object
32440      * @property dragData
32441      */
32442     /**
32443      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
32444      * for auto scrolling during drag operations.
32445      */
32446     /**
32447      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
32448      * method after a failed drop (defaults to "c3daf9" - light blue)
32449      */
32450
32451     /**
32452      * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}
32453      * for a valid target to drag based on the mouse down. Override this method
32454      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
32455      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
32456      * @param {EventObject} e The mouse down event
32457      * @return {Object} The dragData
32458      */
32459     getDragData : function(e){
32460         return Ext.dd.Registry.getHandleFromEvent(e);
32461     },
32462     
32463     /**
32464      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
32465      * this.dragData.ddel
32466      * @param {Number} x The x position of the click on the dragged object
32467      * @param {Number} y The y position of the click on the dragged object
32468      * @return {Boolean} true to continue the drag, false to cancel
32469      */
32470     onInitDrag : function(x, y){
32471         this.proxy.update(this.dragData.ddel.cloneNode(true));
32472         this.onStartDrag(x, y);
32473         return true;
32474     },
32475     
32476     /**
32477      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
32478      */
32479     afterRepair : function(){
32480         if(Ext.enableFx){
32481             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
32482         }
32483         this.dragging = false;
32484     },
32485
32486     /**
32487      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
32488      * the XY of this.dragData.ddel
32489      * @param {EventObject} e The mouse up event
32490      * @return {Array} The xy location (e.g. [100, 200])
32491      */
32492     getRepairXY : function(e){
32493         return Ext.Element.fly(this.dragData.ddel).getXY();  
32494     }
32495 });/**
32496  * @class Ext.dd.DropZone
32497  * @extends Ext.dd.DropTarget
32498  * <p>This class provides a container DD instance that allows dropping on multiple child target nodes.</p>
32499  * <p>By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.
32500  * However a simpler way to allow a DropZone to manage any number of target elements is to configure the
32501  * DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed
32502  * mouse event to see if it has taken place within an element, or class of elements. This is easily done
32503  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
32504  * {@link Ext.DomQuery} selector.</p>
32505  * <p>Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over
32506  * a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},
32507  * {@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations
32508  * of these methods to provide application-specific behaviour for these events to update both
32509  * application state, and UI state.</p>
32510  * <p>For example to make a GridPanel a cooperating target with the example illustrated in
32511  * {@link Ext.dd.DragZone DragZone}, the following technique might be used:</p><pre><code>
32512 myGridPanel.on('render', function() {
32513     myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {
32514
32515 //      If the mouse is over a grid row, return that node. This is
32516 //      provided as the "target" parameter in all "onNodeXXXX" node event handling functions
32517         getTargetFromEvent: function(e) {
32518             return e.getTarget(myGridPanel.getView().rowSelector);
32519         },
32520
32521 //      On entry into a target node, highlight that node.
32522         onNodeEnter : function(target, dd, e, data){ 
32523             Ext.fly(target).addClass('my-row-highlight-class');
32524         },
32525
32526 //      On exit from a target node, unhighlight that node.
32527         onNodeOut : function(target, dd, e, data){ 
32528             Ext.fly(target).removeClass('my-row-highlight-class');
32529         },
32530
32531 //      While over a target node, return the default drop allowed class which
32532 //      places a "tick" icon into the drag proxy.
32533         onNodeOver : function(target, dd, e, data){ 
32534             return Ext.dd.DropZone.prototype.dropAllowed;
32535         },
32536
32537 //      On node drop we can interrogate the target to find the underlying
32538 //      application object that is the real target of the dragged data.
32539 //      In this case, it is a Record in the GridPanel's Store.
32540 //      We can use the data set up by the DragZone's getDragData method to read
32541 //      any data we decided to attach in the DragZone's getDragData method.
32542         onNodeDrop : function(target, dd, e, data){
32543             var rowIndex = myGridPanel.getView().findRowIndex(target);
32544             var r = myGridPanel.getStore().getAt(rowIndex);
32545             Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +
32546                 ' on Record id ' + r.id);
32547             return true;
32548         }
32549     });
32550 }
32551 </code></pre>
32552  * See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which
32553  * cooperates with this DropZone.
32554  * @constructor
32555  * @param {Mixed} el The container element
32556  * @param {Object} config
32557  */
32558 Ext.dd.DropZone = function(el, config){
32559     Ext.dd.DropZone.superclass.constructor.call(this, el, config);
32560 };
32561
32562 Ext.extend(Ext.dd.DropZone, Ext.dd.DropTarget, {
32563     /**
32564      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
32565      * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to
32566      * provide your own custom lookup.
32567      * @param {Event} e The event
32568      * @return {Object} data The custom data
32569      */
32570     getTargetFromEvent : function(e){
32571         return Ext.dd.Registry.getTargetFromEvent(e);
32572     },
32573
32574     /**
32575      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node
32576      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
32577      * This method has no default implementation and should be overridden to provide
32578      * node-specific processing if necessary.
32579      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
32580      * {@link #getTargetFromEvent} for this node)
32581      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32582      * @param {Event} e The event
32583      * @param {Object} data An object containing arbitrary data supplied by the drag source
32584      */
32585     onNodeEnter : function(n, dd, e, data){
32586         
32587     },
32588
32589     /**
32590      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node
32591      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
32592      * The default implementation returns this.dropNotAllowed, so it should be
32593      * overridden to provide the proper feedback.
32594      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
32595      * {@link #getTargetFromEvent} for this node)
32596      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32597      * @param {Event} e The event
32598      * @param {Object} data An object containing arbitrary data supplied by the drag source
32599      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32600      * underlying {@link Ext.dd.StatusProxy} can be updated
32601      */
32602     onNodeOver : function(n, dd, e, data){
32603         return this.dropAllowed;
32604     },
32605
32606     /**
32607      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of
32608      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
32609      * node-specific processing if necessary.
32610      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
32611      * {@link #getTargetFromEvent} for this node)
32612      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32613      * @param {Event} e The event
32614      * @param {Object} data An object containing arbitrary data supplied by the drag source
32615      */
32616     onNodeOut : function(n, dd, e, data){
32617         
32618     },
32619
32620     /**
32621      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto
32622      * the drop node.  The default implementation returns false, so it should be overridden to provide the
32623      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
32624      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
32625      * {@link #getTargetFromEvent} for this node)
32626      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32627      * @param {Event} e The event
32628      * @param {Object} data An object containing arbitrary data supplied by the drag source
32629      * @return {Boolean} True if the drop was valid, else false
32630      */
32631     onNodeDrop : function(n, dd, e, data){
32632         return false;
32633     },
32634
32635     /**
32636      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,
32637      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
32638      * it should be overridden to provide the proper feedback if necessary.
32639      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32640      * @param {Event} e The event
32641      * @param {Object} data An object containing arbitrary data supplied by the drag source
32642      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32643      * underlying {@link Ext.dd.StatusProxy} can be updated
32644      */
32645     onContainerOver : function(dd, e, data){
32646         return this.dropNotAllowed;
32647     },
32648
32649     /**
32650      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,
32651      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
32652      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
32653      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
32654      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32655      * @param {Event} e The event
32656      * @param {Object} data An object containing arbitrary data supplied by the drag source
32657      * @return {Boolean} True if the drop was valid, else false
32658      */
32659     onContainerDrop : function(dd, e, data){
32660         return false;
32661     },
32662
32663     /**
32664      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over
32665      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
32666      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
32667      * you should override this method and provide a custom implementation.
32668      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32669      * @param {Event} e The event
32670      * @param {Object} data An object containing arbitrary data supplied by the drag source
32671      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32672      * underlying {@link Ext.dd.StatusProxy} can be updated
32673      */
32674     notifyEnter : function(dd, e, data){
32675         return this.dropNotAllowed;
32676     },
32677
32678     /**
32679      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.
32680      * This method will be called on every mouse movement while the drag source is over the drop zone.
32681      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
32682      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
32683      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
32684      * registered node, it will call {@link #onContainerOver}.
32685      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32686      * @param {Event} e The event
32687      * @param {Object} data An object containing arbitrary data supplied by the drag source
32688      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32689      * underlying {@link Ext.dd.StatusProxy} can be updated
32690      */
32691     notifyOver : function(dd, e, data){
32692         var n = this.getTargetFromEvent(e);
32693         if(!n){ // not over valid drop target
32694             if(this.lastOverNode){
32695                 this.onNodeOut(this.lastOverNode, dd, e, data);
32696                 this.lastOverNode = null;
32697             }
32698             return this.onContainerOver(dd, e, data);
32699         }
32700         if(this.lastOverNode != n){
32701             if(this.lastOverNode){
32702                 this.onNodeOut(this.lastOverNode, dd, e, data);
32703             }
32704             this.onNodeEnter(n, dd, e, data);
32705             this.lastOverNode = n;
32706         }
32707         return this.onNodeOver(n, dd, e, data);
32708     },
32709
32710     /**
32711      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged
32712      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
32713      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
32714      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32715      * @param {Event} e The event
32716      * @param {Object} data An object containing arbitrary data supplied by the drag zone
32717      */
32718     notifyOut : function(dd, e, data){
32719         if(this.lastOverNode){
32720             this.onNodeOut(this.lastOverNode, dd, e, data);
32721             this.lastOverNode = null;
32722         }
32723     },
32724
32725     /**
32726      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has
32727      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
32728      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
32729      * otherwise it will call {@link #onContainerDrop}.
32730      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32731      * @param {Event} e The event
32732      * @param {Object} data An object containing arbitrary data supplied by the drag source
32733      * @return {Boolean} True if the drop was valid, else false
32734      */
32735     notifyDrop : function(dd, e, data){
32736         if(this.lastOverNode){
32737             this.onNodeOut(this.lastOverNode, dd, e, data);
32738             this.lastOverNode = null;
32739         }
32740         var n = this.getTargetFromEvent(e);
32741         return n ?
32742             this.onNodeDrop(n, dd, e, data) :
32743             this.onContainerDrop(dd, e, data);
32744     },
32745
32746     // private
32747     triggerCacheRefresh : function(){
32748         Ext.dd.DDM.refreshCache(this.groups);
32749     }  
32750 });/**
32751  * @class Ext.Element
32752  */
32753 Ext.Element.addMethods({
32754     /**
32755      * Initializes a {@link Ext.dd.DD} drag drop object for this element.
32756      * @param {String} group The group the DD object is member of
32757      * @param {Object} config The DD config object
32758      * @param {Object} overrides An object containing methods to override/implement on the DD object
32759      * @return {Ext.dd.DD} The DD object
32760      */
32761     initDD : function(group, config, overrides){
32762         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);
32763         return Ext.apply(dd, overrides);
32764     },
32765
32766     /**
32767      * Initializes a {@link Ext.dd.DDProxy} object for this element.
32768      * @param {String} group The group the DDProxy object is member of
32769      * @param {Object} config The DDProxy config object
32770      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
32771      * @return {Ext.dd.DDProxy} The DDProxy object
32772      */
32773     initDDProxy : function(group, config, overrides){
32774         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);
32775         return Ext.apply(dd, overrides);
32776     },
32777
32778     /**
32779      * Initializes a {@link Ext.dd.DDTarget} object for this element.
32780      * @param {String} group The group the DDTarget object is member of
32781      * @param {Object} config The DDTarget config object
32782      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
32783      * @return {Ext.dd.DDTarget} The DDTarget object
32784      */
32785     initDDTarget : function(group, config, overrides){
32786         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);
32787         return Ext.apply(dd, overrides);
32788     }
32789 });
32790 /**
32791  * @class Ext.data.Api
32792  * @extends Object
32793  * Ext.data.Api is a singleton designed to manage the data API including methods
32794  * for validating a developer's DataProxy API.  Defines variables for CRUD actions
32795  * create, read, update and destroy in addition to a mapping of RESTful HTTP methods
32796  * GET, POST, PUT and DELETE to CRUD actions.
32797  * @singleton
32798  */
32799 Ext.data.Api = (function() {
32800
32801     // private validActions.  validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
32802     // Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
32803     // For efficiency, some methods will first check this hash for a match.  Those methods which do acces validActions will cache their result here.
32804     // We cannot pre-define this hash since the developer may over-ride the actions at runtime.
32805     var validActions = {};
32806
32807     return {
32808         /**
32809          * Defined actions corresponding to remote actions:
32810          * <pre><code>
32811 actions: {
32812     create  : 'create',  // Text representing the remote-action to create records on server.
32813     read    : 'read',    // Text representing the remote-action to read/load data from server.
32814     update  : 'update',  // Text representing the remote-action to update records on server.
32815     destroy : 'destroy'  // Text representing the remote-action to destroy records on server.
32816 }
32817          * </code></pre>
32818          * @property actions
32819          * @type Object
32820          */
32821         actions : {
32822             create  : 'create',
32823             read    : 'read',
32824             update  : 'update',
32825             destroy : 'destroy'
32826         },
32827
32828         /**
32829          * Defined {CRUD action}:{HTTP method} pairs to associate HTTP methods with the
32830          * corresponding actions for {@link Ext.data.DataProxy#restful RESTful proxies}.
32831          * Defaults to:
32832          * <pre><code>
32833 restActions : {
32834     create  : 'POST',
32835     read    : 'GET',
32836     update  : 'PUT',
32837     destroy : 'DELETE'
32838 },
32839          * </code></pre>
32840          */
32841         restActions : {
32842             create  : 'POST',
32843             read    : 'GET',
32844             update  : 'PUT',
32845             destroy : 'DELETE'
32846         },
32847
32848         /**
32849          * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
32850          * @param {String} action Action to test for availability.
32851          * @return {Boolean}
32852          */
32853         isAction : function(action) {
32854             return (Ext.data.Api.actions[action]) ? true : false;
32855         },
32856
32857         /**
32858          * Returns the actual CRUD action KEY "create", "read", "update" or "destroy" from the supplied action-name.  This method is used internally and shouldn't generally
32859          * need to be used directly.  The key/value pair of Ext.data.Api.actions will often be identical but this is not necessarily true.  A developer can override this naming
32860          * convention if desired.  However, the framework internally calls methods based upon the KEY so a way of retreiving the the words "create", "read", "update" and "destroy" is
32861          * required.  This method will cache discovered KEYS into the private validActions hash.
32862          * @param {String} name The runtime name of the action.
32863          * @return {String||null} returns the action-key, or verb of the user-action or null if invalid.
32864          * @nodoc
32865          */
32866         getVerb : function(name) {
32867             if (validActions[name]) {
32868                 return validActions[name];  // <-- found in cache.  return immediately.
32869             }
32870             for (var verb in this.actions) {
32871                 if (this.actions[verb] === name) {
32872                     validActions[name] = verb;
32873                     break;
32874                 }
32875             }
32876             return (validActions[name] !== undefined) ? validActions[name] : null;
32877         },
32878
32879         /**
32880          * Returns true if the supplied API is valid; that is, check that all keys match defined actions
32881          * otherwise returns an array of mistakes.
32882          * @return {String[]|true}
32883          */
32884         isValid : function(api){
32885             var invalid = [];
32886             var crud = this.actions; // <-- cache a copy of the actions.
32887             for (var action in api) {
32888                 if (!(action in crud)) {
32889                     invalid.push(action);
32890                 }
32891             }
32892             return (!invalid.length) ? true : invalid;
32893         },
32894
32895         /**
32896          * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions
32897          * point to the same url.  The question is important for deciding whether to insert the "xaction" HTTP parameter within an
32898          * Ajax request.  This method is used internally and shouldn't generally need to be called directly.
32899          * @param {Ext.data.DataProxy} proxy
32900          * @param {String} verb
32901          * @return {Boolean}
32902          */
32903         hasUniqueUrl : function(proxy, verb) {
32904             var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
32905             var unique = true;
32906             for (var action in proxy.api) {
32907                 if ((unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false) {
32908                     break;
32909                 }
32910             }
32911             return unique;
32912         },
32913
32914         /**
32915          * This method is used internally by <tt>{@link Ext.data.DataProxy DataProxy}</tt> and should not generally need to be used directly.
32916          * Each action of a DataProxy api can be initially defined as either a String or an Object.  When specified as an object,
32917          * one can explicitly define the HTTP method (GET|POST) to use for each CRUD action.  This method will prepare the supplied API, setting
32918          * each action to the Object form.  If your API-actions do not explicitly define the HTTP method, the "method" configuration-parameter will
32919          * be used.  If the method configuration parameter is not specified, POST will be used.
32920          <pre><code>
32921 new Ext.data.HttpProxy({
32922     method: "POST",     // <-- default HTTP method when not specified.
32923     api: {
32924         create: 'create.php',
32925         load: 'read.php',
32926         save: 'save.php',
32927         destroy: 'destroy.php'
32928     }
32929 });
32930
32931 // Alternatively, one can use the object-form to specify the API
32932 new Ext.data.HttpProxy({
32933     api: {
32934         load: {url: 'read.php', method: 'GET'},
32935         create: 'create.php',
32936         destroy: 'destroy.php',
32937         save: 'update.php'
32938     }
32939 });
32940         </code></pre>
32941          *
32942          * @param {Ext.data.DataProxy} proxy
32943          */
32944         prepare : function(proxy) {
32945             if (!proxy.api) {
32946                 proxy.api = {}; // <-- No api?  create a blank one.
32947             }
32948             for (var verb in this.actions) {
32949                 var action = this.actions[verb];
32950                 proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
32951                 if (typeof(proxy.api[action]) == 'string') {
32952                     proxy.api[action] = {
32953                         url: proxy.api[action],
32954                         method: (proxy.restful === true) ? Ext.data.Api.restActions[action] : undefined
32955                     };
32956                 }
32957             }
32958         },
32959
32960         /**
32961          * Prepares a supplied Proxy to be RESTful.  Sets the HTTP method for each api-action to be one of
32962          * GET, POST, PUT, DELETE according to the defined {@link #restActions}.
32963          * @param {Ext.data.DataProxy} proxy
32964          */
32965         restify : function(proxy) {
32966             proxy.restful = true;
32967             for (var verb in this.restActions) {
32968                 proxy.api[this.actions[verb]].method ||
32969                     (proxy.api[this.actions[verb]].method = this.restActions[verb]);
32970             }
32971             // TODO: perhaps move this interceptor elsewhere?  like into DataProxy, perhaps?  Placed here
32972             // to satisfy initial 3.0 final release of REST features.
32973             proxy.onWrite = proxy.onWrite.createInterceptor(function(action, o, response, rs) {
32974                 var reader = o.reader;
32975                 var res = new Ext.data.Response({
32976                     action: action,
32977                     raw: response
32978                 });
32979
32980                 switch (response.status) {
32981                     case 200:   // standard 200 response, send control back to HttpProxy#onWrite by returning true from this intercepted #onWrite
32982                         return true;
32983                         break;
32984                     case 201:   // entity created but no response returned
32985                         if (Ext.isEmpty(res.raw.responseText)) {
32986                           res.success = true;
32987                         } else {
32988                           //if the response contains data, treat it like a 200
32989                           return true;
32990                         }
32991                         break;
32992                     case 204:  // no-content.  Create a fake response.
32993                         res.success = true;
32994                         res.data = null;
32995                         break;
32996                     default:
32997                         return true;
32998                         break;
32999                 }
33000                 if (res.success === true) {
33001                     this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
33002                 } else {
33003                     this.fireEvent('exception', this, 'remote', action, o, res, rs);
33004                 }
33005                 o.request.callback.call(o.request.scope, res.data, res, res.success);
33006
33007                 return false;   // <-- false to prevent intercepted function from running.
33008             }, proxy);
33009         }
33010     };
33011 })();
33012
33013 /**
33014  * Ext.data.Response
33015  * Experimental.  Do not use directly.
33016  */
33017 Ext.data.Response = function(params, response) {
33018     Ext.apply(this, params, {
33019         raw: response
33020     });
33021 };
33022 Ext.data.Response.prototype = {
33023     message : null,
33024     success : false,
33025     status : null,
33026     root : null,
33027     raw : null,
33028
33029     getMessage : function() {
33030         return this.message;
33031     },
33032     getSuccess : function() {
33033         return this.success;
33034     },
33035     getStatus : function() {
33036         return this.status;
33037     },
33038     getRoot : function() {
33039         return this.root;
33040     },
33041     getRawResponse : function() {
33042         return this.raw;
33043     }
33044 };
33045
33046 /**
33047  * @class Ext.data.Api.Error
33048  * @extends Ext.Error
33049  * Error class for Ext.data.Api errors
33050  */
33051 Ext.data.Api.Error = Ext.extend(Ext.Error, {
33052     constructor : function(message, arg) {
33053         this.arg = arg;
33054         Ext.Error.call(this, message);
33055     },
33056     name: 'Ext.data.Api'
33057 });
33058 Ext.apply(Ext.data.Api.Error.prototype, {
33059     lang: {
33060         'action-url-undefined': 'No fallback url defined for this action.  When defining a DataProxy api, please be sure to define an url for each CRUD action in Ext.data.Api.actions or define a default url in addition to your api-configuration.',
33061         'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
33062         'invalid-url': 'Invalid url.  Please review your proxy configuration.',
33063         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
33064     }
33065 });
33066
33067
33068
33069 /**
33070  * @class Ext.data.SortTypes
33071  * @singleton
33072  * Defines the default sorting (casting?) comparison functions used when sorting data.
33073  */
33074 Ext.data.SortTypes = {
33075     /**
33076      * Default sort that does nothing
33077      * @param {Mixed} s The value being converted
33078      * @return {Mixed} The comparison value
33079      */
33080     none : function(s){
33081         return s;
33082     },
33083     
33084     /**
33085      * The regular expression used to strip tags
33086      * @type {RegExp}
33087      * @property
33088      */
33089     stripTagsRE : /<\/?[^>]+>/gi,
33090     
33091     /**
33092      * Strips all HTML tags to sort on text only
33093      * @param {Mixed} s The value being converted
33094      * @return {String} The comparison value
33095      */
33096     asText : function(s){
33097         return String(s).replace(this.stripTagsRE, "");
33098     },
33099     
33100     /**
33101      * Strips all HTML tags to sort on text only - Case insensitive
33102      * @param {Mixed} s The value being converted
33103      * @return {String} The comparison value
33104      */
33105     asUCText : function(s){
33106         return String(s).toUpperCase().replace(this.stripTagsRE, "");
33107     },
33108     
33109     /**
33110      * Case insensitive string
33111      * @param {Mixed} s The value being converted
33112      * @return {String} The comparison value
33113      */
33114     asUCString : function(s) {
33115         return String(s).toUpperCase();
33116     },
33117     
33118     /**
33119      * Date sorting
33120      * @param {Mixed} s The value being converted
33121      * @return {Number} The comparison value
33122      */
33123     asDate : function(s) {
33124         if(!s){
33125             return 0;
33126         }
33127         if(Ext.isDate(s)){
33128             return s.getTime();
33129         }
33130         return Date.parse(String(s));
33131     },
33132     
33133     /**
33134      * Float sorting
33135      * @param {Mixed} s The value being converted
33136      * @return {Float} The comparison value
33137      */
33138     asFloat : function(s) {
33139         var val = parseFloat(String(s).replace(/,/g, ""));
33140         return isNaN(val) ? 0 : val;
33141     },
33142     
33143     /**
33144      * Integer sorting
33145      * @param {Mixed} s The value being converted
33146      * @return {Number} The comparison value
33147      */
33148     asInt : function(s) {
33149         var val = parseInt(String(s).replace(/,/g, ""), 10);
33150         return isNaN(val) ? 0 : val;
33151     }
33152 };/**
33153  * @class Ext.data.Record
33154  * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record
33155  * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs
33156  * to access Records cached in an {@link Ext.data.Store} object.</p>
33157  * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
33158  * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data
33159  * objects.</p>
33160  * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time.
33161  * In order to copy data from one Store to another, use the {@link #copy} method to create an exact
33162  * copy of the Record, and insert the new instance into the other Store.</p>
33163  * <p>When serializing a Record for submission to the server, be aware that it contains many private
33164  * properties, and also a reference to its owning Store which in turn holds references to its Records.
33165  * This means that a whole Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the
33166  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
33167  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
33168  * @constructor
33169  * <p>This constructor should not be used to create Record objects. Instead, use {@link #create} to
33170  * generate a subclass of Ext.data.Record configured with information about its constituent fields.<p>
33171  * <p><b>The generated constructor has the same signature as this constructor.</b></p>
33172  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
33173  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
33174  * for each field will be assigned.
33175  * @param {Object} id (Optional) The id of the Record. The id is used by the
33176  * {@link Ext.data.Store} object which owns the Record to index its collection
33177  * of Records (therefore this id should be unique within each store). If an
33178  * <code>id</code> is not specified a <b><code>{@link #phantom}</code></b>
33179  * Record will be created with an {@link #Record.id automatically generated id}.
33180  */
33181 Ext.data.Record = function(data, id){
33182     // if no id, call the auto id method
33183     this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
33184     this.data = data || {};
33185 };
33186
33187 /**
33188  * Generate a constructor for a specific Record layout.
33189  * @param {Array} o An Array of <b>{@link Ext.data.Field Field}</b> definition objects.
33190  * The constructor generated by this method may be used to create new Record instances. The data
33191  * object must contain properties named after the {@link Ext.data.Field field}
33192  * <b><tt>{@link Ext.data.Field#name}s</tt></b>.  Example usage:<pre><code>
33193 // create a Record constructor from a description of the fields
33194 var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
33195     {{@link Ext.data.Field#name name}: 'title', {@link Ext.data.Field#mapping mapping}: 'topic_title'},
33196     {name: 'author', mapping: 'username', allowBlank: false},
33197     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
33198     {name: 'lastPost', mapping: 'post_time', type: 'date'},
33199     {name: 'lastPoster', mapping: 'user2'},
33200     {name: 'excerpt', mapping: 'post_text', allowBlank: false},
33201     // In the simplest case, if no properties other than <tt>name</tt> are required,
33202     // a field definition may consist of just a String for the field name.
33203     'signature'
33204 ]);
33205
33206 // create Record instance
33207 var myNewRecord = new TopicRecord(
33208     {
33209         title: 'Do my job please',
33210         author: 'noobie',
33211         totalPosts: 1,
33212         lastPost: new Date(),
33213         lastPoster: 'Animal',
33214         excerpt: 'No way dude!',
33215         signature: ''
33216     },
33217     id // optionally specify the id of the record otherwise {@link #Record.id one is auto-assigned}
33218 );
33219 myStore.{@link Ext.data.Store#add add}(myNewRecord);
33220 </code></pre>
33221  * @method create
33222  * @return {Function} A constructor which is used to create new Records according
33223  * to the definition. The constructor has the same signature as {@link #Record}.
33224  * @static
33225  */
33226 Ext.data.Record.create = function(o){
33227     var f = Ext.extend(Ext.data.Record, {});
33228     var p = f.prototype;
33229     p.fields = new Ext.util.MixedCollection(false, function(field){
33230         return field.name;
33231     });
33232     for(var i = 0, len = o.length; i < len; i++){
33233         p.fields.add(new Ext.data.Field(o[i]));
33234     }
33235     f.getField = function(name){
33236         return p.fields.get(name);
33237     };
33238     return f;
33239 };
33240
33241 Ext.data.Record.PREFIX = 'ext-record';
33242 Ext.data.Record.AUTO_ID = 1;
33243 Ext.data.Record.EDIT = 'edit';
33244 Ext.data.Record.REJECT = 'reject';
33245 Ext.data.Record.COMMIT = 'commit';
33246
33247
33248 /**
33249  * Generates a sequential id. This method is typically called when a record is {@link #create}d
33250  * and {@link #Record no id has been specified}. The returned id takes the form:
33251  * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
33252  * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.PREFIX</tt>
33253  * (defaults to <tt>'ext-record'</tt>)</p></li>
33254  * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.AUTO_ID</tt>
33255  * (defaults to <tt>1</tt> initially)</p></li>
33256  * </ul></div>
33257  * @param {Record} rec The record being created.  The record does not exist, it's a {@link #phantom}.
33258  * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
33259  */
33260 Ext.data.Record.id = function(rec) {
33261     rec.phantom = true;
33262     return [Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++].join('');
33263 };
33264
33265 Ext.data.Record.prototype = {
33266     /**
33267      * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p>
33268      * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record.  Read-only.
33269      * @property fields
33270      * @type Ext.util.MixedCollection
33271      */
33272     /**
33273      * An object hash representing the data for this Record. Every field name in the Record definition
33274      * is represented by a property of that name in this object. Note that unless you specified a field
33275      * with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain
33276      * an <tt>id</tt> property.
33277      * @property data
33278      * @type {Object}
33279      */
33280     /**
33281      * The unique ID of the Record {@link #Record as specified at construction time}.
33282      * @property id
33283      * @type {Object}
33284      */
33285     /**
33286      * <p><b>Only present if this Record was created by an {@link Ext.data.XmlReader XmlReader}</b>.</p>
33287      * <p>The XML element which was the source of the data for this Record.</p>
33288      * @property node
33289      * @type {XMLElement}
33290      */
33291     /**
33292      * <p><b>Only present if this Record was created by an {@link Ext.data.ArrayReader ArrayReader} or a {@link Ext.data.JsonReader JsonReader}</b>.</p>
33293      * <p>The Array or object which was the source of the data for this Record.</p>
33294      * @property json
33295      * @type {Array|Object}
33296      */
33297     /**
33298      * Readonly flag - true if this Record has been modified.
33299      * @type Boolean
33300      */
33301     dirty : false,
33302     editing : false,
33303     error : null,
33304     /**
33305      * This object contains a key and value storing the original values of all modified
33306      * fields or is null if no fields have been modified.
33307      * @property modified
33308      * @type {Object}
33309      */
33310     modified : null,
33311     /**
33312      * <tt>true</tt> when the record does not yet exist in a server-side database (see
33313      * {@link #markDirty}).  Any record which has a real database pk set as its id property
33314      * is NOT a phantom -- it's real.
33315      * @property phantom
33316      * @type {Boolean}
33317      */
33318     phantom : false,
33319
33320     // private
33321     join : function(store){
33322         /**
33323          * The {@link Ext.data.Store} to which this Record belongs.
33324          * @property store
33325          * @type {Ext.data.Store}
33326          */
33327         this.store = store;
33328     },
33329
33330     /**
33331      * Set the {@link Ext.data.Field#name named field} to the specified value.  For example:
33332      * <pre><code>
33333 // record has a field named 'firstname'
33334 var Employee = Ext.data.Record.{@link #create}([
33335     {name: 'firstname'},
33336     ...
33337 ]);
33338
33339 // update the 2nd record in the store:
33340 var rec = myStore.{@link Ext.data.Store#getAt getAt}(1);
33341
33342 // set the value (shows dirty flag):
33343 rec.set('firstname', 'Betty');
33344
33345 // commit the change (removes dirty flag):
33346 rec.{@link #commit}();
33347
33348 // update the record in the store, bypass setting dirty flag,
33349 // and do not store the change in the {@link Ext.data.Store#getModifiedRecords modified records}
33350 rec.{@link #data}['firstname'] = 'Wilma'; // updates record, but not the view
33351 rec.{@link #commit}(); // updates the view
33352      * </code></pre>
33353      * <b>Notes</b>:<div class="mdetail-params"><ul>
33354      * <li>If the store has a writer and <code>autoSave=true</code>, each set()
33355      * will execute an XHR to the server.</li>
33356      * <li>Use <code>{@link #beginEdit}</code> to prevent the store's <code>update</code>
33357      * event firing while using set().</li>
33358      * <li>Use <code>{@link #endEdit}</code> to have the store's <code>update</code>
33359      * event fire.</li>
33360      * </ul></div>
33361      * @param {String} name The {@link Ext.data.Field#name name of the field} to set.
33362      * @param {String/Object/Array} value The value to set the field to.
33363      */
33364     set : function(name, value){
33365         var encode = Ext.isPrimitive(value) ? String : Ext.encode;
33366         if(encode(this.data[name]) == encode(value)) {
33367             return;
33368         }        
33369         this.dirty = true;
33370         if(!this.modified){
33371             this.modified = {};
33372         }
33373         if(this.modified[name] === undefined){
33374             this.modified[name] = this.data[name];
33375         }
33376         this.data[name] = value;
33377         if(!this.editing){
33378             this.afterEdit();
33379         }
33380     },
33381
33382     // private
33383     afterEdit : function(){
33384         if (this.store != undefined && typeof this.store.afterEdit == "function") {
33385             this.store.afterEdit(this);
33386         }
33387     },
33388
33389     // private
33390     afterReject : function(){
33391         if(this.store){
33392             this.store.afterReject(this);
33393         }
33394     },
33395
33396     // private
33397     afterCommit : function(){
33398         if(this.store){
33399             this.store.afterCommit(this);
33400         }
33401     },
33402
33403     /**
33404      * Get the value of the {@link Ext.data.Field#name named field}.
33405      * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
33406      * @return {Object} The value of the field.
33407      */
33408     get : function(name){
33409         return this.data[name];
33410     },
33411
33412     /**
33413      * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
33414      * are relayed to the containing store.
33415      * See also: <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
33416      */
33417     beginEdit : function(){
33418         this.editing = true;
33419         this.modified = this.modified || {};
33420     },
33421
33422     /**
33423      * Cancels all changes made in the current edit operation.
33424      */
33425     cancelEdit : function(){
33426         this.editing = false;
33427         delete this.modified;
33428     },
33429
33430     /**
33431      * End an edit. If any data was modified, the containing store is notified
33432      * (ie, the store's <code>update</code> event will fire).
33433      */
33434     endEdit : function(){
33435         this.editing = false;
33436         if(this.dirty){
33437             this.afterEdit();
33438         }
33439     },
33440
33441     /**
33442      * Usually called by the {@link Ext.data.Store} which owns the Record.
33443      * Rejects all changes made to the Record since either creation, or the last commit operation.
33444      * Modified fields are reverted to their original values.
33445      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
33446      * to have their code notified of reject operations.</p>
33447      * @param {Boolean} silent (optional) True to skip notification of the owning
33448      * store of the change (defaults to false)
33449      */
33450     reject : function(silent){
33451         var m = this.modified;
33452         for(var n in m){
33453             if(typeof m[n] != "function"){
33454                 this.data[n] = m[n];
33455             }
33456         }
33457         this.dirty = false;
33458         delete this.modified;
33459         this.editing = false;
33460         if(silent !== true){
33461             this.afterReject();
33462         }
33463     },
33464
33465     /**
33466      * Usually called by the {@link Ext.data.Store} which owns the Record.
33467      * Commits all changes made to the Record since either creation, or the last commit operation.
33468      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
33469      * to have their code notified of commit operations.</p>
33470      * @param {Boolean} silent (optional) True to skip notification of the owning
33471      * store of the change (defaults to false)
33472      */
33473     commit : function(silent){
33474         this.dirty = false;
33475         delete this.modified;
33476         this.editing = false;
33477         if(silent !== true){
33478             this.afterCommit();
33479         }
33480     },
33481
33482     /**
33483      * Gets a hash of only the fields that have been modified since this Record was created or commited.
33484      * @return Object
33485      */
33486     getChanges : function(){
33487         var m = this.modified, cs = {};
33488         for(var n in m){
33489             if(m.hasOwnProperty(n)){
33490                 cs[n] = this.data[n];
33491             }
33492         }
33493         return cs;
33494     },
33495
33496     // private
33497     hasError : function(){
33498         return this.error !== null;
33499     },
33500
33501     // private
33502     clearError : function(){
33503         this.error = null;
33504     },
33505
33506     /**
33507      * Creates a copy (clone) of this Record.
33508      * @param {String} id (optional) A new Record id, defaults to the id
33509      * of the record being copied. See <code>{@link #id}</code>. 
33510      * To generate a phantom record with a new id use:<pre><code>
33511 var rec = record.copy(); // clone the record
33512 Ext.data.Record.id(rec); // automatically generate a unique sequential id
33513      * </code></pre>
33514      * @return {Record}
33515      */
33516     copy : function(newId) {
33517         return new this.constructor(Ext.apply({}, this.data), newId || this.id);
33518     },
33519
33520     /**
33521      * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
33522      * since the load or last commit.
33523      * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
33524      * @return {Boolean}
33525      */
33526     isModified : function(fieldName){
33527         return !!(this.modified && this.modified.hasOwnProperty(fieldName));
33528     },
33529
33530     /**
33531      * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the
33532      * record configured with <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns
33533      * <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
33534      * @return {Boolean}
33535      */
33536     isValid : function() {
33537         return this.fields.find(function(f) {
33538             return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
33539         },this) ? false : true;
33540     },
33541
33542     /**
33543      * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
33544      * is used interally when adding <code>{@link #phantom}</code> records to a
33545      * {@link Ext.data.Store#writer writer enabled store}.</p>
33546      * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
33547      * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
33548      * have a create action composed for it during {@link Ext.data.Store#save store save}
33549      * operations.</p>
33550      */
33551     markDirty : function(){
33552         this.dirty = true;
33553         if(!this.modified){
33554             this.modified = {};
33555         }
33556         this.fields.each(function(f) {
33557             this.modified[f.name] = this.data[f.name];
33558         },this);
33559     }
33560 };
33561 /**
33562  * @class Ext.StoreMgr
33563  * @extends Ext.util.MixedCollection
33564  * The default global group of stores.
33565  * @singleton
33566  */
33567 Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
33568     /**
33569      * @cfg {Object} listeners @hide
33570      */
33571
33572     /**
33573      * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
33574      * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
33575      * @param {Ext.data.Store} store1 A Store instance
33576      * @param {Ext.data.Store} store2 (optional)
33577      * @param {Ext.data.Store} etc... (optional)
33578      */
33579     register : function(){
33580         for(var i = 0, s; (s = arguments[i]); i++){
33581             this.add(s);
33582         }
33583     },
33584
33585     /**
33586      * Unregisters one or more Stores with the StoreMgr
33587      * @param {String/Object} id1 The id of the Store, or a Store instance
33588      * @param {String/Object} id2 (optional)
33589      * @param {String/Object} etc... (optional)
33590      */
33591     unregister : function(){
33592         for(var i = 0, s; (s = arguments[i]); i++){
33593             this.remove(this.lookup(s));
33594         }
33595     },
33596
33597     /**
33598      * Gets a registered Store by id
33599      * @param {String/Object} id The id of the Store, or a Store instance
33600      * @return {Ext.data.Store}
33601      */
33602     lookup : function(id){
33603         if(Ext.isArray(id)){
33604             var fields = ['field1'], expand = !Ext.isArray(id[0]);
33605             if(!expand){
33606                 for(var i = 2, len = id[0].length; i <= len; ++i){
33607                     fields.push('field' + i);
33608                 }
33609             }
33610             return new Ext.data.ArrayStore({
33611                 fields: fields,
33612                 data: id,
33613                 expandData: expand,
33614                 autoDestroy: true,
33615                 autoCreated: true
33616
33617             });
33618         }
33619         return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
33620     },
33621
33622     // getKey implementation for MixedCollection
33623     getKey : function(o){
33624          return o.storeId;
33625     }
33626 });/**
33627  * @class Ext.data.Store
33628  * @extends Ext.util.Observable
33629  * <p>The Store class encapsulates a client side cache of {@link Ext.data.Record Record}
33630  * objects which provide input data for Components such as the {@link Ext.grid.GridPanel GridPanel},
33631  * the {@link Ext.form.ComboBox ComboBox}, or the {@link Ext.DataView DataView}.</p>
33632  * <p><u>Retrieving Data</u></p>
33633  * <p>A Store object may access a data object using:<div class="mdetail-params"><ul>
33634  * <li>{@link #proxy configured implementation} of {@link Ext.data.DataProxy DataProxy}</li>
33635  * <li>{@link #data} to automatically pass in data</li>
33636  * <li>{@link #loadData} to manually pass in data</li>
33637  * </ul></div></p>
33638  * <p><u>Reading Data</u></p>
33639  * <p>A Store object has no inherent knowledge of the format of the data object (it could be
33640  * an Array, XML, or JSON). A Store object uses an appropriate {@link #reader configured implementation}
33641  * of a {@link Ext.data.DataReader DataReader} to create {@link Ext.data.Record Record} instances from the data
33642  * object.</p>
33643  * <p><u>Store Types</u></p>
33644  * <p>There are several implementations of Store available which are customized for use with
33645  * a specific DataReader implementation.  Here is an example using an ArrayStore which implicitly
33646  * creates a reader commensurate to an Array data object.</p>
33647  * <pre><code>
33648 var myStore = new Ext.data.ArrayStore({
33649     fields: ['fullname', 'first'],
33650     idIndex: 0 // id for each record will be the first element
33651 });
33652  * </code></pre>
33653  * <p>For custom implementations create a basic {@link Ext.data.Store} configured as needed:</p>
33654  * <pre><code>
33655 // create a {@link Ext.data.Record Record} constructor:
33656 var rt = Ext.data.Record.create([
33657     {name: 'fullname'},
33658     {name: 'first'}
33659 ]);
33660 var myStore = new Ext.data.Store({
33661     // explicitly create reader
33662     reader: new Ext.data.ArrayReader(
33663         {
33664             idIndex: 0  // id for each record will be the first element
33665         },
33666         rt // recordType
33667     )
33668 });
33669  * </code></pre>
33670  * <p>Load some data into store (note the data object is an array which corresponds to the reader):</p>
33671  * <pre><code>
33672 var myData = [
33673     [1, 'Fred Flintstone', 'Fred'],  // note that id for the record is the first element
33674     [2, 'Barney Rubble', 'Barney']
33675 ];
33676 myStore.loadData(myData);
33677  * </code></pre>
33678  * <p>Records are cached and made available through accessor functions.  An example of adding
33679  * a record to the store:</p>
33680  * <pre><code>
33681 var defaultData = {
33682     fullname: 'Full Name',
33683     first: 'First Name'
33684 };
33685 var recId = 100; // provide unique id for the record
33686 var r = new myStore.recordType(defaultData, ++recId); // create new record
33687 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
33688  * </code></pre>
33689  * <p><u>Writing Data</u></p>
33690  * <p>And <b>new in Ext version 3</b>, use the new {@link Ext.data.DataWriter DataWriter} to create an automated, <a href="http://extjs.com/deploy/dev/examples/writer/writer.html">Writable Store</a>
33691  * along with <a href="http://extjs.com/deploy/dev/examples/restful/restful.html">RESTful features.</a>
33692  * @constructor
33693  * Creates a new Store.
33694  * @param {Object} config A config object containing the objects needed for the Store to access data,
33695  * and read the data into Records.
33696  * @xtype store
33697  */
33698 Ext.data.Store = Ext.extend(Ext.util.Observable, {
33699     /**
33700      * @cfg {String} storeId If passed, the id to use to register with the <b>{@link Ext.StoreMgr StoreMgr}</b>.
33701      * <p><b>Note</b>: if a (deprecated) <tt>{@link #id}</tt> is specified it will supersede the <tt>storeId</tt>
33702      * assignment.</p>
33703      */
33704     /**
33705      * @cfg {String} url If a <tt>{@link #proxy}</tt> is not specified the <tt>url</tt> will be used to
33706      * implicitly configure a {@link Ext.data.HttpProxy HttpProxy} if an <tt>url</tt> is specified.
33707      * Typically this option, or the <code>{@link #data}</code> option will be specified.
33708      */
33709     /**
33710      * @cfg {Boolean/Object} autoLoad If <tt>{@link #data}</tt> is not specified, and if <tt>autoLoad</tt>
33711      * is <tt>true</tt> or an <tt>Object</tt>, this store's {@link #load} method is automatically called
33712      * after creation. If the value of <tt>autoLoad</tt> is an <tt>Object</tt>, this <tt>Object</tt> will
33713      * be passed to the store's {@link #load} method.
33714      */
33715     /**
33716      * @cfg {Ext.data.DataProxy} proxy The {@link Ext.data.DataProxy DataProxy} object which provides
33717      * access to a data object.  See <code>{@link #url}</code>.
33718      */
33719     /**
33720      * @cfg {Array} data An inline data object readable by the <code>{@link #reader}</code>.
33721      * Typically this option, or the <code>{@link #url}</code> option will be specified.
33722      */
33723     /**
33724      * @cfg {Ext.data.DataReader} reader The {@link Ext.data.DataReader Reader} object which processes the
33725      * data object and returns an Array of {@link Ext.data.Record} objects which are cached keyed by their
33726      * <b><tt>{@link Ext.data.Record#id id}</tt></b> property.
33727      */
33728     /**
33729      * @cfg {Ext.data.DataWriter} writer
33730      * <p>The {@link Ext.data.DataWriter Writer} object which processes a record object for being written
33731      * to the server-side database.</p>
33732      * <br><p>When a writer is installed into a Store the {@link #add}, {@link #remove}, and {@link #update}
33733      * events on the store are monitored in order to remotely {@link #createRecords create records},
33734      * {@link #destroyRecord destroy records}, or {@link #updateRecord update records}.</p>
33735      * <br><p>The proxy for this store will relay any {@link #writexception} events to this store.</p>
33736      * <br><p>Sample implementation:
33737      * <pre><code>
33738 var writer = new {@link Ext.data.JsonWriter}({
33739     encode: true,
33740     writeAllFields: true // write all fields, not just those that changed
33741 });
33742
33743 // Typical Store collecting the Proxy, Reader and Writer together.
33744 var store = new Ext.data.Store({
33745     storeId: 'user',
33746     root: 'records',
33747     proxy: proxy,
33748     reader: reader,
33749     writer: writer,     // <-- plug a DataWriter into the store just as you would a Reader
33750     paramsAsHash: true,
33751     autoSave: false    // <-- false to delay executing create, update, destroy requests
33752                         //     until specifically told to do so.
33753 });
33754      * </code></pre></p>
33755      */
33756     writer : undefined,
33757     /**
33758      * @cfg {Object} baseParams
33759      * <p>An object containing properties which are to be sent as parameters
33760      * for <i>every</i> HTTP request.</p>
33761      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
33762      * <p><b>Note</b>: <code>baseParams</code> may be superseded by any <code>params</code>
33763      * specified in a <code>{@link #load}</code> request, see <code>{@link #load}</code>
33764      * for more details.</p>
33765      * This property may be modified after creation using the <code>{@link #setBaseParam}</code>
33766      * method.
33767      * @property
33768      */
33769     /**
33770      * @cfg {Object} sortInfo A config object to specify the sort order in the request of a Store's
33771      * {@link #load} operation.  Note that for local sorting, the <tt>direction</tt> property is
33772      * case-sensitive. See also {@link #remoteSort} and {@link #paramNames}.
33773      * For example:<pre><code>
33774 sortInfo: {
33775     field: 'fieldName',
33776     direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
33777 }
33778 </code></pre>
33779      */
33780     /**
33781      * @cfg {boolean} remoteSort <tt>true</tt> if sorting is to be handled by requesting the <tt>{@link #proxy Proxy}</tt>
33782      * to provide a refreshed version of the data object in sorted order, as opposed to sorting the Record cache
33783      * in place (defaults to <tt>false</tt>).
33784      * <p>If <tt>remoteSort</tt> is <tt>true</tt>, then clicking on a {@link Ext.grid.Column Grid Column}'s
33785      * {@link Ext.grid.Column#header header} causes the current page to be requested from the server appending
33786      * the following two parameters to the <b><tt>{@link #load params}</tt></b>:<div class="mdetail-params"><ul>
33787      * <li><b><tt>sort</tt></b> : String<p class="sub-desc">The <tt>name</tt> (as specified in the Record's
33788      * {@link Ext.data.Field Field definition}) of the field to sort on.</p></li>
33789      * <li><b><tt>dir</tt></b> : String<p class="sub-desc">The direction of the sort, 'ASC' or 'DESC' (case-sensitive).</p></li>
33790      * </ul></div></p>
33791      */
33792     remoteSort : false,
33793
33794     /**
33795      * @cfg {Boolean} autoDestroy <tt>true</tt> to destroy the store when the component the store is bound
33796      * to is destroyed (defaults to <tt>false</tt>).
33797      * <p><b>Note</b>: this should be set to true when using stores that are bound to only 1 component.</p>
33798      */
33799     autoDestroy : false,
33800
33801     /**
33802      * @cfg {Boolean} pruneModifiedRecords <tt>true</tt> to clear all modified record information each time
33803      * the store is loaded or when a record is removed (defaults to <tt>false</tt>). See {@link #getModifiedRecords}
33804      * for the accessor method to retrieve the modified records.
33805      */
33806     pruneModifiedRecords : false,
33807
33808     /**
33809      * Contains the last options object used as the parameter to the {@link #load} method. See {@link #load}
33810      * for the details of what this may contain. This may be useful for accessing any params which were used
33811      * to load the current Record cache.
33812      * @property
33813      */
33814     lastOptions : null,
33815
33816     /**
33817      * @cfg {Boolean} autoSave
33818      * <p>Defaults to <tt>true</tt> causing the store to automatically {@link #save} records to
33819      * the server when a record is modified (ie: becomes 'dirty'). Specify <tt>false</tt> to manually call {@link #save}
33820      * to send all modifiedRecords to the server.</p>
33821      * <br><p><b>Note</b>: each CRUD action will be sent as a separate request.</p>
33822      */
33823     autoSave : true,
33824
33825     /**
33826      * @cfg {Boolean} batch
33827      * <p>Defaults to <tt>true</tt> (unless <code>{@link #restful}:true</code>). Multiple
33828      * requests for each CRUD action (CREATE, READ, UPDATE and DESTROY) will be combined
33829      * and sent as one transaction. Only applies when <code>{@link #autoSave}</code> is set
33830      * to <tt>false</tt>.</p>
33831      * <br><p>If Store is RESTful, the DataProxy is also RESTful, and a unique transaction is
33832      * generated for each record.</p>
33833      */
33834     batch : true,
33835
33836     /**
33837      * @cfg {Boolean} restful
33838      * Defaults to <tt>false</tt>.  Set to <tt>true</tt> to have the Store and the set
33839      * Proxy operate in a RESTful manner. The store will automatically generate GET, POST,
33840      * PUT and DELETE requests to the server. The HTTP method used for any given CRUD
33841      * action is described in {@link Ext.data.Api#restActions}.  For additional information
33842      * see {@link Ext.data.DataProxy#restful}.
33843      * <p><b>Note</b>: if <code>{@link #restful}:true</code> <code>batch</code> will
33844      * internally be set to <tt>false</tt>.</p>
33845      */
33846     restful: false,
33847
33848     /**
33849      * @cfg {Object} paramNames
33850      * <p>An object containing properties which specify the names of the paging and
33851      * sorting parameters passed to remote servers when loading blocks of data. By default, this
33852      * object takes the following form:</p><pre><code>
33853 {
33854     start : 'start',  // The parameter name which specifies the start row
33855     limit : 'limit',  // The parameter name which specifies number of rows to return
33856     sort : 'sort',    // The parameter name which specifies the column to sort on
33857     dir : 'dir'       // The parameter name which specifies the sort direction
33858 }
33859 </code></pre>
33860      * <p>The server must produce the requested data block upon receipt of these parameter names.
33861      * If different parameter names are required, this property can be overriden using a configuration
33862      * property.</p>
33863      * <p>A {@link Ext.PagingToolbar PagingToolbar} bound to this Store uses this property to determine
33864      * the parameter names to use in its {@link #load requests}.
33865      */
33866     paramNames : undefined,
33867
33868     /**
33869      * @cfg {Object} defaultParamNames
33870      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
33871      * for all stores, this object should be changed on the store prototype.
33872      */
33873     defaultParamNames : {
33874         start : 'start',
33875         limit : 'limit',
33876         sort : 'sort',
33877         dir : 'dir'
33878     },
33879
33880     /**
33881      * @property isDestroyed
33882      * @type Boolean
33883      * True if the store has been destroyed already. Read only
33884      */
33885     isDestroyed: false,
33886
33887     /**
33888      * @property hasMultiSort
33889      * @type Boolean
33890      * True if this store is currently sorted by more than one field/direction combination.
33891      */
33892     hasMultiSort: false,
33893
33894     // private
33895     batchKey : '_ext_batch_',
33896
33897     constructor : function(config){
33898         this.data = new Ext.util.MixedCollection(false);
33899         this.data.getKey = function(o){
33900             return o.id;
33901         };
33902
33903
33904         // temporary removed-records cache
33905         this.removed = [];
33906
33907         if(config && config.data){
33908             this.inlineData = config.data;
33909             delete config.data;
33910         }
33911
33912         Ext.apply(this, config);
33913
33914         /**
33915          * See the <code>{@link #baseParams corresponding configuration option}</code>
33916          * for a description of this property.
33917          * To modify this property see <code>{@link #setBaseParam}</code>.
33918          * @property
33919          */
33920         this.baseParams = Ext.isObject(this.baseParams) ? this.baseParams : {};
33921
33922         this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
33923
33924         if((this.url || this.api) && !this.proxy){
33925             this.proxy = new Ext.data.HttpProxy({url: this.url, api: this.api});
33926         }
33927         // If Store is RESTful, so too is the DataProxy
33928         if (this.restful === true && this.proxy) {
33929             // When operating RESTfully, a unique transaction is generated for each record.
33930             // TODO might want to allow implemention of faux REST where batch is possible using RESTful routes only.
33931             this.batch = false;
33932             Ext.data.Api.restify(this.proxy);
33933         }
33934
33935         if(this.reader){ // reader passed
33936             if(!this.recordType){
33937                 this.recordType = this.reader.recordType;
33938             }
33939             if(this.reader.onMetaChange){
33940                 this.reader.onMetaChange = this.reader.onMetaChange.createSequence(this.onMetaChange, this);
33941             }
33942             if (this.writer) { // writer passed
33943                 if (this.writer instanceof(Ext.data.DataWriter) === false) {    // <-- config-object instead of instance.
33944                     this.writer = this.buildWriter(this.writer);
33945                 }
33946                 this.writer.meta = this.reader.meta;
33947                 this.pruneModifiedRecords = true;
33948             }
33949         }
33950
33951         /**
33952          * The {@link Ext.data.Record Record} constructor as supplied to (or created by) the
33953          * {@link Ext.data.DataReader Reader}. Read-only.
33954          * <p>If the Reader was constructed by passing in an Array of {@link Ext.data.Field} definition objects,
33955          * instead of a Record constructor, it will implicitly create a Record constructor from that Array (see
33956          * {@link Ext.data.Record}.{@link Ext.data.Record#create create} for additional details).</p>
33957          * <p>This property may be used to create new Records of the type held in this Store, for example:</p><pre><code>
33958     // create the data store
33959     var store = new Ext.data.ArrayStore({
33960         autoDestroy: true,
33961         fields: [
33962            {name: 'company'},
33963            {name: 'price', type: 'float'},
33964            {name: 'change', type: 'float'},
33965            {name: 'pctChange', type: 'float'},
33966            {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
33967         ]
33968     });
33969     store.loadData(myData);
33970
33971     // create the Grid
33972     var grid = new Ext.grid.EditorGridPanel({
33973         store: store,
33974         colModel: new Ext.grid.ColumnModel({
33975             columns: [
33976                 {id:'company', header: 'Company', width: 160, dataIndex: 'company'},
33977                 {header: 'Price', renderer: 'usMoney', dataIndex: 'price'},
33978                 {header: 'Change', renderer: change, dataIndex: 'change'},
33979                 {header: '% Change', renderer: pctChange, dataIndex: 'pctChange'},
33980                 {header: 'Last Updated', width: 85,
33981                     renderer: Ext.util.Format.dateRenderer('m/d/Y'),
33982                     dataIndex: 'lastChange'}
33983             ],
33984             defaults: {
33985                 sortable: true,
33986                 width: 75
33987             }
33988         }),
33989         autoExpandColumn: 'company', // match the id specified in the column model
33990         height:350,
33991         width:600,
33992         title:'Array Grid',
33993         tbar: [{
33994             text: 'Add Record',
33995             handler : function(){
33996                 var defaultData = {
33997                     change: 0,
33998                     company: 'New Company',
33999                     lastChange: (new Date()).clearTime(),
34000                     pctChange: 0,
34001                     price: 10
34002                 };
34003                 var recId = 3; // provide unique id
34004                 var p = new store.recordType(defaultData, recId); // create new record
34005                 grid.stopEditing();
34006                 store.{@link #insert}(0, p); // insert a new record into the store (also see {@link #add})
34007                 grid.startEditing(0, 0);
34008             }
34009         }]
34010     });
34011          * </code></pre>
34012          * @property recordType
34013          * @type Function
34014          */
34015
34016         if(this.recordType){
34017             /**
34018              * A {@link Ext.util.MixedCollection MixedCollection} containing the defined {@link Ext.data.Field Field}s
34019              * for the {@link Ext.data.Record Records} stored in this Store. Read-only.
34020              * @property fields
34021              * @type Ext.util.MixedCollection
34022              */
34023             this.fields = this.recordType.prototype.fields;
34024         }
34025         this.modified = [];
34026
34027         this.addEvents(
34028             /**
34029              * @event datachanged
34030              * Fires when the data cache has changed in a bulk manner (e.g., it has been sorted, filtered, etc.) and a
34031              * widget that is using this Store as a Record cache should refresh its view.
34032              * @param {Store} this
34033              */
34034             'datachanged',
34035             /**
34036              * @event metachange
34037              * Fires when this store's reader provides new metadata (fields). This is currently only supported for JsonReaders.
34038              * @param {Store} this
34039              * @param {Object} meta The JSON metadata
34040              */
34041             'metachange',
34042             /**
34043              * @event add
34044              * Fires when Records have been {@link #add}ed to the Store
34045              * @param {Store} this
34046              * @param {Ext.data.Record[]} records The array of Records added
34047              * @param {Number} index The index at which the record(s) were added
34048              */
34049             'add',
34050             /**
34051              * @event remove
34052              * Fires when a Record has been {@link #remove}d from the Store
34053              * @param {Store} this
34054              * @param {Ext.data.Record} record The Record that was removed
34055              * @param {Number} index The index at which the record was removed
34056              */
34057             'remove',
34058             /**
34059              * @event update
34060              * Fires when a Record has been updated
34061              * @param {Store} this
34062              * @param {Ext.data.Record} record The Record that was updated
34063              * @param {String} operation The update operation being performed.  Value may be one of:
34064              * <pre><code>
34065      Ext.data.Record.EDIT
34066      Ext.data.Record.REJECT
34067      Ext.data.Record.COMMIT
34068              * </code></pre>
34069              */
34070             'update',
34071             /**
34072              * @event clear
34073              * Fires when the data cache has been cleared.
34074              * @param {Store} this
34075              * @param {Record[]} The records that were cleared.
34076              */
34077             'clear',
34078             /**
34079              * @event exception
34080              * <p>Fires if an exception occurs in the Proxy during a remote request.
34081              * This event is relayed through the corresponding {@link Ext.data.DataProxy}.
34082              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
34083              * for additional details.
34084              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
34085              * for description.
34086              */
34087             'exception',
34088             /**
34089              * @event beforeload
34090              * Fires before a request is made for a new data object.  If the beforeload handler returns
34091              * <tt>false</tt> the {@link #load} action will be canceled.
34092              * @param {Store} this
34093              * @param {Object} options The loading options that were specified (see {@link #load} for details)
34094              */
34095             'beforeload',
34096             /**
34097              * @event load
34098              * Fires after a new set of Records has been loaded.
34099              * @param {Store} this
34100              * @param {Ext.data.Record[]} records The Records that were loaded
34101              * @param {Object} options The loading options that were specified (see {@link #load} for details)
34102              */
34103             'load',
34104             /**
34105              * @event loadexception
34106              * <p>This event is <b>deprecated</b> in favor of the catch-all <b><code>{@link #exception}</code></b>
34107              * event instead.</p>
34108              * <p>This event is relayed through the corresponding {@link Ext.data.DataProxy}.
34109              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
34110              * for additional details.
34111              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
34112              * for description.
34113              */
34114             'loadexception',
34115             /**
34116              * @event beforewrite
34117              * @param {Ext.data.Store} store
34118              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
34119              * @param {Record/Record[]} rs The Record(s) being written.
34120              * @param {Object} options The loading options that were specified. Edit <code>options.params</code> to add Http parameters to the request.  (see {@link #save} for details)
34121              * @param {Object} arg The callback's arg object passed to the {@link #request} function
34122              */
34123             'beforewrite',
34124             /**
34125              * @event write
34126              * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
34127              * Success of the action is determined in the <code>result['successProperty']</code>property (<b>NOTE</b> for RESTful stores,
34128              * a simple 20x response is sufficient for the actions "destroy" and "update".  The "create" action should should return 200 along with a database pk).
34129              * @param {Ext.data.Store} store
34130              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
34131              * @param {Object} result The 'data' picked-out out of the response for convenience.
34132              * @param {Ext.Direct.Transaction} res
34133              * @param {Record/Record[]} rs Store's records, the subject(s) of the write-action
34134              */
34135             'write',
34136             /**
34137              * @event beforesave
34138              * Fires before a save action is called. A save encompasses destroying records, updating records and creating records.
34139              * @param {Ext.data.Store} store
34140              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
34141              * with an array of records for each action.
34142              */
34143             'beforesave',
34144             /**
34145              * @event save
34146              * Fires after a save is completed. A save encompasses destroying records, updating records and creating records.
34147              * @param {Ext.data.Store} store
34148              * @param {Number} batch The identifier for the batch that was saved.
34149              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
34150              * with an array of records for each action.
34151              */
34152             'save'
34153
34154         );
34155
34156         if(this.proxy){
34157             // TODO remove deprecated loadexception with ext-3.0.1
34158             this.relayEvents(this.proxy,  ['loadexception', 'exception']);
34159         }
34160         // With a writer set for the Store, we want to listen to add/remove events to remotely create/destroy records.
34161         if (this.writer) {
34162             this.on({
34163                 scope: this,
34164                 add: this.createRecords,
34165                 remove: this.destroyRecord,
34166                 update: this.updateRecord,
34167                 clear: this.onClear
34168             });
34169         }
34170
34171         this.sortToggle = {};
34172         if(this.sortField){
34173             this.setDefaultSort(this.sortField, this.sortDir);
34174         }else if(this.sortInfo){
34175             this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
34176         }
34177
34178         Ext.data.Store.superclass.constructor.call(this);
34179
34180         if(this.id){
34181             this.storeId = this.id;
34182             delete this.id;
34183         }
34184         if(this.storeId){
34185             Ext.StoreMgr.register(this);
34186         }
34187         if(this.inlineData){
34188             this.loadData(this.inlineData);
34189             delete this.inlineData;
34190         }else if(this.autoLoad){
34191             this.load.defer(10, this, [
34192                 typeof this.autoLoad == 'object' ?
34193                     this.autoLoad : undefined]);
34194         }
34195         // used internally to uniquely identify a batch
34196         this.batchCounter = 0;
34197         this.batches = {};
34198     },
34199
34200     /**
34201      * builds a DataWriter instance when Store constructor is provided with a writer config-object instead of an instace.
34202      * @param {Object} config Writer configuration
34203      * @return {Ext.data.DataWriter}
34204      * @private
34205      */
34206     buildWriter : function(config) {
34207         var klass = undefined,
34208             type = (config.format || 'json').toLowerCase();
34209         switch (type) {
34210             case 'json':
34211                 klass = Ext.data.JsonWriter;
34212                 break;
34213             case 'xml':
34214                 klass = Ext.data.XmlWriter;
34215                 break;
34216             default:
34217                 klass = Ext.data.JsonWriter;
34218         }
34219         return new klass(config);
34220     },
34221
34222     /**
34223      * Destroys the store.
34224      */
34225     destroy : function(){
34226         if(!this.isDestroyed){
34227             if(this.storeId){
34228                 Ext.StoreMgr.unregister(this);
34229             }
34230             this.clearData();
34231             this.data = null;
34232             Ext.destroy(this.proxy);
34233             this.reader = this.writer = null;
34234             this.purgeListeners();
34235             this.isDestroyed = true;
34236         }
34237     },
34238
34239     /**
34240      * Add Records to the Store and fires the {@link #add} event.  To add Records
34241      * to the store from a remote source use <code>{@link #load}({add:true})</code>.
34242      * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
34243      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
34244      * to add to the cache. See {@link #recordType}.
34245      */
34246     add : function(records){
34247         records = [].concat(records);
34248         if(records.length < 1){
34249             return;
34250         }
34251         for(var i = 0, len = records.length; i < len; i++){
34252             records[i].join(this);
34253         }
34254         var index = this.data.length;
34255         this.data.addAll(records);
34256         if(this.snapshot){
34257             this.snapshot.addAll(records);
34258         }
34259         this.fireEvent('add', this, records, index);
34260     },
34261
34262     /**
34263      * (Local sort only) Inserts the passed Record into the Store at the index where it
34264      * should go based on the current sort information.
34265      * @param {Ext.data.Record} record
34266      */
34267     addSorted : function(record){
34268         var index = this.findInsertIndex(record);
34269         this.insert(index, record);
34270     },
34271
34272     /**
34273      * Remove Records from the Store and fires the {@link #remove} event.
34274      * @param {Ext.data.Record/Ext.data.Record[]} record The record object or array of records to remove from the cache.
34275      */
34276     remove : function(record){
34277         if(Ext.isArray(record)){
34278             Ext.each(record, function(r){
34279                 this.remove(r);
34280             }, this);
34281         }
34282         var index = this.data.indexOf(record);
34283         if(index > -1){
34284             record.join(null);
34285             this.data.removeAt(index);
34286         }
34287         if(this.pruneModifiedRecords){
34288             this.modified.remove(record);
34289         }
34290         if(this.snapshot){
34291             this.snapshot.remove(record);
34292         }
34293         if(index > -1){
34294             this.fireEvent('remove', this, record, index);
34295         }
34296     },
34297
34298     /**
34299      * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
34300      * @param {Number} index The index of the record to remove.
34301      */
34302     removeAt : function(index){
34303         this.remove(this.getAt(index));
34304     },
34305
34306     /**
34307      * Remove all Records from the Store and fires the {@link #clear} event.
34308      * @param {Boolean} silent [false] Defaults to <tt>false</tt>.  Set <tt>true</tt> to not fire clear event.
34309      */
34310     removeAll : function(silent){
34311         var items = [];
34312         this.each(function(rec){
34313             items.push(rec);
34314         });
34315         this.clearData();
34316         if(this.snapshot){
34317             this.snapshot.clear();
34318         }
34319         if(this.pruneModifiedRecords){
34320             this.modified = [];
34321         }
34322         if (silent !== true) {  // <-- prevents write-actions when we just want to clear a store.
34323             this.fireEvent('clear', this, items);
34324         }
34325     },
34326
34327     // private
34328     onClear: function(store, records){
34329         Ext.each(records, function(rec, index){
34330             this.destroyRecord(this, rec, index);
34331         }, this);
34332     },
34333
34334     /**
34335      * Inserts Records into the Store at the given index and fires the {@link #add} event.
34336      * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
34337      * @param {Number} index The start index at which to insert the passed Records.
34338      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
34339      */
34340     insert : function(index, records){
34341         records = [].concat(records);
34342         for(var i = 0, len = records.length; i < len; i++){
34343             this.data.insert(index, records[i]);
34344             records[i].join(this);
34345         }
34346         if(this.snapshot){
34347             this.snapshot.addAll(records);
34348         }
34349         this.fireEvent('add', this, records, index);
34350     },
34351
34352     /**
34353      * Get the index within the cache of the passed Record.
34354      * @param {Ext.data.Record} record The Ext.data.Record object to find.
34355      * @return {Number} The index of the passed Record. Returns -1 if not found.
34356      */
34357     indexOf : function(record){
34358         return this.data.indexOf(record);
34359     },
34360
34361     /**
34362      * Get the index within the cache of the Record with the passed id.
34363      * @param {String} id The id of the Record to find.
34364      * @return {Number} The index of the Record. Returns -1 if not found.
34365      */
34366     indexOfId : function(id){
34367         return this.data.indexOfKey(id);
34368     },
34369
34370     /**
34371      * Get the Record with the specified id.
34372      * @param {String} id The id of the Record to find.
34373      * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
34374      */
34375     getById : function(id){
34376         return (this.snapshot || this.data).key(id);
34377     },
34378
34379     /**
34380      * Get the Record at the specified index.
34381      * @param {Number} index The index of the Record to find.
34382      * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
34383      */
34384     getAt : function(index){
34385         return this.data.itemAt(index);
34386     },
34387
34388     /**
34389      * Returns a range of Records between specified indices.
34390      * @param {Number} startIndex (optional) The starting index (defaults to 0)
34391      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
34392      * @return {Ext.data.Record[]} An array of Records
34393      */
34394     getRange : function(start, end){
34395         return this.data.getRange(start, end);
34396     },
34397
34398     // private
34399     storeOptions : function(o){
34400         o = Ext.apply({}, o);
34401         delete o.callback;
34402         delete o.scope;
34403         this.lastOptions = o;
34404     },
34405
34406     // private
34407     clearData: function(){
34408         this.data.each(function(rec) {
34409             rec.join(null);
34410         });
34411         this.data.clear();
34412     },
34413
34414     /**
34415      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
34416      * <br><p>Notes:</p><div class="mdetail-params"><ul>
34417      * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
34418      * loaded. To perform any post-processing where information from the load call is required, specify
34419      * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
34420      * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
34421      * properties in the <code>options.params</code> property to establish the initial position within the
34422      * dataset, and the number of Records to cache on each read from the Proxy.</li>
34423      * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
34424      * will be automatically included with the posted parameters according to the specified
34425      * <code>{@link #paramNames}</code>.</li>
34426      * </ul></div>
34427      * @param {Object} options An object containing properties which control loading options:<ul>
34428      * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
34429      * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
34430      * <code>{@link #baseParams}</code> of the same name.</p>
34431      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
34432      * <li><b>callback</b> : Function<div class="sub-desc"><p>A function to be called after the Records
34433      * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
34434      * <li>r : Ext.data.Record[] An Array of Records loaded.</li>
34435      * <li>options : Options object from the load call.</li>
34436      * <li>success : Boolean success indicator.</li></ul></p></div></li>
34437      * <li><b>scope</b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
34438      * to the Store object)</p></div></li>
34439      * <li><b>add</b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
34440      * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
34441      * </ul>
34442      * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
34443      * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
34444      */
34445     load : function(options) {
34446         options = Ext.apply({}, options);
34447         this.storeOptions(options);
34448         if(this.sortInfo && this.remoteSort){
34449             var pn = this.paramNames;
34450             options.params = Ext.apply({}, options.params);
34451             options.params[pn.sort] = this.sortInfo.field;
34452             options.params[pn.dir] = this.sortInfo.direction;
34453         }
34454         try {
34455             return this.execute('read', null, options); // <-- null represents rs.  No rs for load actions.
34456         } catch(e) {
34457             this.handleException(e);
34458             return false;
34459         }
34460     },
34461
34462     /**
34463      * updateRecord  Should not be used directly.  This method will be called automatically if a Writer is set.
34464      * Listens to 'update' event.
34465      * @param {Object} store
34466      * @param {Object} record
34467      * @param {Object} action
34468      * @private
34469      */
34470     updateRecord : function(store, record, action) {
34471         if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid()))) {
34472             this.save();
34473         }
34474     },
34475
34476     /**
34477      * Should not be used directly.  Store#add will call this automatically if a Writer is set
34478      * @param {Object} store
34479      * @param {Object} rs
34480      * @param {Object} index
34481      * @private
34482      */
34483     createRecords : function(store, rs, index) {
34484         for (var i = 0, len = rs.length; i < len; i++) {
34485             if (rs[i].phantom && rs[i].isValid()) {
34486                 rs[i].markDirty();  // <-- Mark new records dirty
34487                 this.modified.push(rs[i]);  // <-- add to modified
34488             }
34489         }
34490         if (this.autoSave === true) {
34491             this.save();
34492         }
34493     },
34494
34495     /**
34496      * Destroys a Record.  Should not be used directly.  It's called by Store#remove if a Writer is set.
34497      * @param {Store} store this
34498      * @param {Ext.data.Record} record
34499      * @param {Number} index
34500      * @private
34501      */
34502     destroyRecord : function(store, record, index) {
34503         if (this.modified.indexOf(record) != -1) {  // <-- handled already if @cfg pruneModifiedRecords == true
34504             this.modified.remove(record);
34505         }
34506         if (!record.phantom) {
34507             this.removed.push(record);
34508
34509             // since the record has already been removed from the store but the server request has not yet been executed,
34510             // must keep track of the last known index this record existed.  If a server error occurs, the record can be
34511             // put back into the store.  @see Store#createCallback where the record is returned when response status === false
34512             record.lastIndex = index;
34513
34514             if (this.autoSave === true) {
34515                 this.save();
34516             }
34517         }
34518     },
34519
34520     /**
34521      * This method should generally not be used directly.  This method is called internally
34522      * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
34523      * {@link #remove}, or {@link #update} events fire.
34524      * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
34525      * @param {Record/Record[]} rs
34526      * @param {Object} options
34527      * @throws Error
34528      * @private
34529      */
34530     execute : function(action, rs, options, /* private */ batch) {
34531         // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
34532         if (!Ext.data.Api.isAction(action)) {
34533             throw new Ext.data.Api.Error('execute', action);
34534         }
34535         // make sure options has a fresh, new params hash
34536         options = Ext.applyIf(options||{}, {
34537             params: {}
34538         });
34539         if(batch !== undefined){
34540             this.addToBatch(batch);
34541         }
34542         // have to separate before-events since load has a different signature than create,destroy and save events since load does not
34543         // include the rs (record resultset) parameter.  Capture return values from the beforeaction into doRequest flag.
34544         var doRequest = true;
34545
34546         if (action === 'read') {
34547             doRequest = this.fireEvent('beforeload', this, options);
34548             Ext.applyIf(options.params, this.baseParams);
34549         }
34550         else {
34551             // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
34552             // TODO Move listful rendering into DataWriter where the @cfg is defined.  Should be easy now.
34553             if (this.writer.listful === true && this.restful !== true) {
34554                 rs = (Ext.isArray(rs)) ? rs : [rs];
34555             }
34556             // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
34557             else if (Ext.isArray(rs) && rs.length == 1) {
34558                 rs = rs.shift();
34559             }
34560             // Write the action to options.params
34561             if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
34562                 this.writer.apply(options.params, this.baseParams, action, rs);
34563             }
34564         }
34565         if (doRequest !== false) {
34566             // Send request to proxy.
34567             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
34568                 options.params.xaction = action;    // <-- really old, probaby unecessary.
34569             }
34570             // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
34571             // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
34572             // the user's configured DataProxy#api
34573             // TODO Refactor all Proxies to accept an instance of Ext.data.Request (not yet defined) instead of this looooooong list
34574             // of params.  This method is an artifact from Ext2.
34575             this.proxy.request(Ext.data.Api.actions[action], rs, options.params, this.reader, this.createCallback(action, rs, batch), this, options);
34576         }
34577         return doRequest;
34578     },
34579
34580     /**
34581      * Saves all pending changes to the store.  If the commensurate Ext.data.Api.actions action is not configured, then
34582      * the configured <code>{@link #url}</code> will be used.
34583      * <pre>
34584      * change            url
34585      * ---------------   --------------------
34586      * removed records   Ext.data.Api.actions.destroy
34587      * phantom records   Ext.data.Api.actions.create
34588      * {@link #getModifiedRecords modified records}  Ext.data.Api.actions.update
34589      * </pre>
34590      * @TODO:  Create extensions of Error class and send associated Record with thrown exceptions.
34591      * e.g.:  Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
34592      * @return {Number} batch Returns a number to uniquely identify the "batch" of saves occurring. -1 will be returned
34593      * if there are no items to save or the save was cancelled.
34594      */
34595     save : function() {
34596         if (!this.writer) {
34597             throw new Ext.data.Store.Error('writer-undefined');
34598         }
34599
34600         var queue = [],
34601             len,
34602             trans,
34603             batch,
34604             data = {};
34605         // DESTROY:  First check for removed records.  Records in this.removed are guaranteed non-phantoms.  @see Store#remove
34606         if(this.removed.length){
34607             queue.push(['destroy', this.removed]);
34608         }
34609
34610         // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
34611         var rs = [].concat(this.getModifiedRecords());
34612         if(rs.length){
34613             // CREATE:  Next check for phantoms within rs.  splice-off and execute create.
34614             var phantoms = [];
34615             for(var i = rs.length-1; i >= 0; i--){
34616                 if(rs[i].phantom === true){
34617                     var rec = rs.splice(i, 1).shift();
34618                     if(rec.isValid()){
34619                         phantoms.push(rec);
34620                     }
34621                 }else if(!rs[i].isValid()){ // <-- while we're here, splice-off any !isValid real records
34622                     rs.splice(i,1);
34623                 }
34624             }
34625             // If we have valid phantoms, create them...
34626             if(phantoms.length){
34627                 queue.push(['create', phantoms]);
34628             }
34629
34630             // UPDATE:  And finally, if we're still here after splicing-off phantoms and !isValid real records, update the rest...
34631             if(rs.length){
34632                 queue.push(['update', rs]);
34633             }
34634         }
34635         len = queue.length;
34636         if(len){
34637             batch = ++this.batchCounter;
34638             for(var i = 0; i < len; ++i){
34639                 trans = queue[i];
34640                 data[trans[0]] = trans[1];
34641             }
34642             if(this.fireEvent('beforesave', this, data) !== false){
34643                 for(var i = 0; i < len; ++i){
34644                     trans = queue[i];
34645                     this.doTransaction(trans[0], trans[1], batch);
34646                 }
34647                 return batch;
34648             }
34649         }
34650         return -1;
34651     },
34652
34653     // private.  Simply wraps call to Store#execute in try/catch.  Defers to Store#handleException on error.  Loops if batch: false
34654     doTransaction : function(action, rs, batch) {
34655         function transaction(records) {
34656             try{
34657                 this.execute(action, records, undefined, batch);
34658             }catch (e){
34659                 this.handleException(e);
34660             }
34661         }
34662         if(this.batch === false){
34663             for(var i = 0, len = rs.length; i < len; i++){
34664                 transaction.call(this, rs[i]);
34665             }
34666         }else{
34667             transaction.call(this, rs);
34668         }
34669     },
34670
34671     // private
34672     addToBatch : function(batch){
34673         var b = this.batches,
34674             key = this.batchKey + batch,
34675             o = b[key];
34676
34677         if(!o){
34678             b[key] = o = {
34679                 id: batch,
34680                 count: 0,
34681                 data: {}
34682             };
34683         }
34684         ++o.count;
34685     },
34686
34687     removeFromBatch : function(batch, action, data){
34688         var b = this.batches,
34689             key = this.batchKey + batch,
34690             o = b[key],
34691             data,
34692             arr;
34693
34694
34695         if(o){
34696             arr = o.data[action] || [];
34697             o.data[action] = arr.concat(data);
34698             if(o.count === 1){
34699                 data = o.data;
34700                 delete b[key];
34701                 this.fireEvent('save', this, batch, data);
34702             }else{
34703                 --o.count;
34704             }
34705         }
34706     },
34707
34708     // @private callback-handler for remote CRUD actions
34709     // Do not override -- override loadRecords, onCreateRecords, onDestroyRecords and onUpdateRecords instead.
34710     createCallback : function(action, rs, batch) {
34711         var actions = Ext.data.Api.actions;
34712         return (action == 'read') ? this.loadRecords : function(data, response, success) {
34713             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
34714             this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, [].concat(data));
34715             // If success === false here, exception will have been called in DataProxy
34716             if (success === true) {
34717                 this.fireEvent('write', this, action, data, response, rs);
34718             }
34719             this.removeFromBatch(batch, action, data);
34720         };
34721     },
34722
34723     // Clears records from modified array after an exception event.
34724     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
34725     // TODO remove this method?
34726     clearModified : function(rs) {
34727         if (Ext.isArray(rs)) {
34728             for (var n=rs.length-1;n>=0;n--) {
34729                 this.modified.splice(this.modified.indexOf(rs[n]), 1);
34730             }
34731         } else {
34732             this.modified.splice(this.modified.indexOf(rs), 1);
34733         }
34734     },
34735
34736     // remap record ids in MixedCollection after records have been realized.  @see Store#onCreateRecords, @see DataReader#realize
34737     reMap : function(record) {
34738         if (Ext.isArray(record)) {
34739             for (var i = 0, len = record.length; i < len; i++) {
34740                 this.reMap(record[i]);
34741             }
34742         } else {
34743             delete this.data.map[record._phid];
34744             this.data.map[record.id] = record;
34745             var index = this.data.keys.indexOf(record._phid);
34746             this.data.keys.splice(index, 1, record.id);
34747             delete record._phid;
34748         }
34749     },
34750
34751     // @protected onCreateRecord proxy callback for create action
34752     onCreateRecords : function(success, rs, data) {
34753         if (success === true) {
34754             try {
34755                 this.reader.realize(rs, data);
34756                 this.reMap(rs);
34757             }
34758             catch (e) {
34759                 this.handleException(e);
34760                 if (Ext.isArray(rs)) {
34761                     // Recurse to run back into the try {}.  DataReader#realize splices-off the rs until empty.
34762                     this.onCreateRecords(success, rs, data);
34763                 }
34764             }
34765         }
34766     },
34767
34768     // @protected, onUpdateRecords proxy callback for update action
34769     onUpdateRecords : function(success, rs, data) {
34770         if (success === true) {
34771             try {
34772                 this.reader.update(rs, data);
34773             } catch (e) {
34774                 this.handleException(e);
34775                 if (Ext.isArray(rs)) {
34776                     // Recurse to run back into the try {}.  DataReader#update splices-off the rs until empty.
34777                     this.onUpdateRecords(success, rs, data);
34778                 }
34779             }
34780         }
34781     },
34782
34783     // @protected onDestroyRecords proxy callback for destroy action
34784     onDestroyRecords : function(success, rs, data) {
34785         // splice each rec out of this.removed
34786         rs = (rs instanceof Ext.data.Record) ? [rs] : [].concat(rs);
34787         for (var i=0,len=rs.length;i<len;i++) {
34788             this.removed.splice(this.removed.indexOf(rs[i]), 1);
34789         }
34790         if (success === false) {
34791             // put records back into store if remote destroy fails.
34792             // @TODO: Might want to let developer decide.
34793             for (i=rs.length-1;i>=0;i--) {
34794                 this.insert(rs[i].lastIndex, rs[i]);    // <-- lastIndex set in Store#destroyRecord
34795             }
34796         }
34797     },
34798
34799     // protected handleException.  Possibly temporary until Ext framework has an exception-handler.
34800     handleException : function(e) {
34801         // @see core/Error.js
34802         Ext.handleError(e);
34803     },
34804
34805     /**
34806      * <p>Reloads the Record cache from the configured Proxy using the configured
34807      * {@link Ext.data.Reader Reader} and the options from the last load operation
34808      * performed.</p>
34809      * <p><b>Note</b>: see the Important note in {@link #load}.</p>
34810      * @param {Object} options <p>(optional) An <tt>Object</tt> containing
34811      * {@link #load loading options} which may override the {@link #lastOptions options}
34812      * used in the last {@link #load} operation. See {@link #load} for details
34813      * (defaults to <tt>null</tt>, in which case the {@link #lastOptions} are
34814      * used).</p>
34815      * <br><p>To add new params to the existing params:</p><pre><code>
34816 lastOptions = myStore.lastOptions;
34817 Ext.apply(lastOptions.params, {
34818     myNewParam: true
34819 });
34820 myStore.reload(lastOptions);
34821      * </code></pre>
34822      */
34823     reload : function(options){
34824         this.load(Ext.applyIf(options||{}, this.lastOptions));
34825     },
34826
34827     // private
34828     // Called as a callback by the Reader during a load operation.
34829     loadRecords : function(o, options, success){
34830         if (this.isDestroyed === true) {
34831             return;
34832         }
34833         if(!o || success === false){
34834             if(success !== false){
34835                 this.fireEvent('load', this, [], options);
34836             }
34837             if(options.callback){
34838                 options.callback.call(options.scope || this, [], options, false, o);
34839             }
34840             return;
34841         }
34842         var r = o.records, t = o.totalRecords || r.length;
34843         if(!options || options.add !== true){
34844             if(this.pruneModifiedRecords){
34845                 this.modified = [];
34846             }
34847             for(var i = 0, len = r.length; i < len; i++){
34848                 r[i].join(this);
34849             }
34850             if(this.snapshot){
34851                 this.data = this.snapshot;
34852                 delete this.snapshot;
34853             }
34854             this.clearData();
34855             this.data.addAll(r);
34856             this.totalLength = t;
34857             this.applySort();
34858             this.fireEvent('datachanged', this);
34859         }else{
34860             this.totalLength = Math.max(t, this.data.length+r.length);
34861             this.add(r);
34862         }
34863         this.fireEvent('load', this, r, options);
34864         if(options.callback){
34865             options.callback.call(options.scope || this, r, options, true);
34866         }
34867     },
34868
34869     /**
34870      * Loads data from a passed data block and fires the {@link #load} event. A {@link Ext.data.Reader Reader}
34871      * which understands the format of the data must have been configured in the constructor.
34872      * @param {Object} data The data block from which to read the Records.  The format of the data expected
34873      * is dependent on the type of {@link Ext.data.Reader Reader} that is configured and should correspond to
34874      * that {@link Ext.data.Reader Reader}'s <tt>{@link Ext.data.Reader#readRecords}</tt> parameter.
34875      * @param {Boolean} append (Optional) <tt>true</tt> to append the new Records rather the default to replace
34876      * the existing cache.
34877      * <b>Note</b>: that Records in a Store are keyed by their {@link Ext.data.Record#id id}, so added Records
34878      * with ids which are already present in the Store will <i>replace</i> existing Records. Only Records with
34879      * new, unique ids will be added.
34880      */
34881     loadData : function(o, append){
34882         var r = this.reader.readRecords(o);
34883         this.loadRecords(r, {add: append}, true);
34884     },
34885
34886     /**
34887      * Gets the number of cached records.
34888      * <p>If using paging, this may not be the total size of the dataset. If the data object
34889      * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
34890      * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
34891      * @return {Number} The number of Records in the Store's cache.
34892      */
34893     getCount : function(){
34894         return this.data.length || 0;
34895     },
34896
34897     /**
34898      * Gets the total number of records in the dataset as returned by the server.
34899      * <p>If using paging, for this to be accurate, the data object used by the {@link #reader Reader}
34900      * must contain the dataset size. For remote data sources, the value for this property
34901      * (<tt>totalProperty</tt> for {@link Ext.data.JsonReader JsonReader},
34902      * <tt>totalRecords</tt> for {@link Ext.data.XmlReader XmlReader}) shall be returned by a query on the server.
34903      * <b>Note</b>: see the Important note in {@link #load}.</p>
34904      * @return {Number} The number of Records as specified in the data object passed to the Reader
34905      * by the Proxy.
34906      * <p><b>Note</b>: this value is not updated when changing the contents of the Store locally.</p>
34907      */
34908     getTotalCount : function(){
34909         return this.totalLength || 0;
34910     },
34911
34912     /**
34913      * Returns an object describing the current sort state of this Store.
34914      * @return {Object} The sort state of the Store. An object with two properties:<ul>
34915      * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
34916      * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
34917      * </ul>
34918      * See <tt>{@link #sortInfo}</tt> for additional details.
34919      */
34920     getSortState : function(){
34921         return this.sortInfo;
34922     },
34923
34924     /**
34925      * @private
34926      * Invokes sortData if we have sortInfo to sort on and are not sorting remotely
34927      */
34928     applySort : function(){
34929         if ((this.sortInfo || this.multiSortInfo) && !this.remoteSort) {
34930             this.sortData();
34931         }
34932     },
34933
34934     /**
34935      * @private
34936      * Performs the actual sorting of data. This checks to see if we currently have a multi sort or not. It applies
34937      * each sorter field/direction pair in turn by building an OR'ed master sorting function and running it against
34938      * the full dataset
34939      */
34940     sortData : function() {
34941         var sortInfo  = this.hasMultiSort ? this.multiSortInfo : this.sortInfo,
34942             direction = sortInfo.direction || "ASC",
34943             sorters   = sortInfo.sorters,
34944             sortFns   = [];
34945
34946         //if we just have a single sorter, pretend it's the first in an array
34947         if (!this.hasMultiSort) {
34948             sorters = [{direction: direction, field: sortInfo.field}];
34949         }
34950
34951         //create a sorter function for each sorter field/direction combo
34952         for (var i=0, j = sorters.length; i < j; i++) {
34953             sortFns.push(this.createSortFunction(sorters[i].field, sorters[i].direction));
34954         }
34955         
34956         if (sortFns.length == 0) {
34957             return;
34958         }
34959
34960         //the direction modifier is multiplied with the result of the sorting functions to provide overall sort direction
34961         //(as opposed to direction per field)
34962         var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
34963
34964         //create a function which ORs each sorter together to enable multi-sort
34965         var fn = function(r1, r2) {
34966           var result = sortFns[0].call(this, r1, r2);
34967
34968           //if we have more than one sorter, OR any additional sorter functions together
34969           if (sortFns.length > 1) {
34970               for (var i=1, j = sortFns.length; i < j; i++) {
34971                   result = result || sortFns[i].call(this, r1, r2);
34972               }
34973           }
34974
34975           return directionModifier * result;
34976         };
34977
34978         //sort the data
34979         this.data.sort(direction, fn);
34980         if (this.snapshot && this.snapshot != this.data) {
34981             this.snapshot.sort(direction, fn);
34982         }
34983     },
34984
34985     /**
34986      * Creates and returns a function which sorts an array by the given field and direction
34987      * @param {String} field The field to create the sorter for
34988      * @param {String} direction The direction to sort by (defaults to "ASC")
34989      * @return {Function} A function which sorts by the field/direction combination provided
34990      */
34991     createSortFunction: function(field, direction) {
34992         direction = direction || "ASC";
34993         var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
34994
34995         var sortType = this.fields.get(field).sortType;
34996
34997         //create a comparison function. Takes 2 records, returns 1 if record 1 is greater,
34998         //-1 if record 2 is greater or 0 if they are equal
34999         return function(r1, r2) {
35000             var v1 = sortType(r1.data[field]),
35001                 v2 = sortType(r2.data[field]);
35002
35003             return directionModifier * (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
35004         };
35005     },
35006
35007     /**
35008      * Sets the default sort column and order to be used by the next {@link #load} operation.
35009      * @param {String} fieldName The name of the field to sort by.
35010      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
35011      */
35012     setDefaultSort : function(field, dir) {
35013         dir = dir ? dir.toUpperCase() : 'ASC';
35014         this.sortInfo = {field: field, direction: dir};
35015         this.sortToggle[field] = dir;
35016     },
35017
35018     /**
35019      * Sort the Records.
35020      * If remote sorting is used, the sort is performed on the server, and the cache is reloaded. If local
35021      * sorting is used, the cache is sorted internally. See also {@link #remoteSort} and {@link #paramNames}.
35022      * This function accepts two call signatures - pass in a field name as the first argument to sort on a single
35023      * field, or pass in an array of sort configuration objects to sort by multiple fields.
35024      * Single sort example:
35025      * store.sort('name', 'ASC');
35026      * Multi sort example:
35027      * store.sort([
35028      *   {
35029      *     field    : 'name',
35030      *     direction: 'ASC'
35031      *   },
35032      *   {
35033      *     field    : 'salary',
35034      *     direction: 'DESC'
35035      *   }
35036      * ], 'ASC');
35037      * In this second form, the sort configs are applied in order, with later sorters sorting within earlier sorters' results.
35038      * For example, if two records with the same name are present they will also be sorted by salary if given the sort configs
35039      * above. Any number of sort configs can be added.
35040      * @param {String/Array} fieldName The name of the field to sort by, or an array of ordered sort configs
35041      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
35042      */
35043     sort : function(fieldName, dir) {
35044         if (Ext.isArray(arguments[0])) {
35045             return this.multiSort.call(this, fieldName, dir);
35046         } else {
35047             return this.singleSort(fieldName, dir);
35048         }
35049     },
35050
35051     /**
35052      * Sorts the store contents by a single field and direction. This is called internally by {@link sort} and would
35053      * not usually be called manually
35054      * @param {String} fieldName The name of the field to sort by.
35055      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
35056      */
35057     singleSort: function(fieldName, dir) {
35058         var field = this.fields.get(fieldName);
35059         if (!field) return false;
35060
35061         var name       = field.name,
35062             sortInfo   = this.sortInfo || null,
35063             sortToggle = this.sortToggle ? this.sortToggle[name] : null;
35064
35065         if (!dir) {
35066             if (sortInfo && sortInfo.field == name) { // toggle sort dir
35067                 dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
35068             } else {
35069                 dir = field.sortDir;
35070             }
35071         }
35072
35073         this.sortToggle[name] = dir;
35074         this.sortInfo = {field: name, direction: dir};
35075         this.hasMultiSort = false;
35076
35077         if (this.remoteSort) {
35078             if (!this.load(this.lastOptions)) {
35079                 if (sortToggle) {
35080                     this.sortToggle[name] = sortToggle;
35081                 }
35082                 if (sortInfo) {
35083                     this.sortInfo = sortInfo;
35084                 }
35085             }
35086         } else {
35087             this.applySort();
35088             this.fireEvent('datachanged', this);
35089         }
35090     },
35091
35092     /**
35093      * Sorts the contents of this store by multiple field/direction sorters. This is called internally by {@link sort}
35094      * and would not usually be called manually.
35095      * Multi sorting only currently applies to local datasets - multiple sort data is not currently sent to a proxy
35096      * if remoteSort is used.
35097      * @param {Array} sorters Array of sorter objects (field and direction)
35098      * @param {String} direction Overall direction to sort the ordered results by (defaults to "ASC")
35099      */
35100     multiSort: function(sorters, direction) {
35101         this.hasMultiSort = true;
35102         direction = direction || "ASC";
35103
35104         //toggle sort direction
35105         if (this.multiSortInfo && direction == this.multiSortInfo.direction) {
35106             direction = direction.toggle("ASC", "DESC");
35107         }
35108
35109         /**
35110          * @property multiSortInfo
35111          * @type Object
35112          * Object containing overall sort direction and an ordered array of sorter configs used when sorting on multiple fields
35113          */
35114         this.multiSortInfo = {
35115             sorters  : sorters,
35116             direction: direction
35117         };
35118         
35119         if (this.remoteSort) {
35120             this.singleSort(sorters[0].field, sorters[0].direction);
35121
35122         } else {
35123             this.applySort();
35124             this.fireEvent('datachanged', this);
35125         }
35126     },
35127
35128     /**
35129      * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
35130      * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
35131      * Returning <tt>false</tt> aborts and exits the iteration.
35132      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
35133      * Defaults to the current {@link Ext.data.Record Record} in the iteration.
35134      */
35135     each : function(fn, scope){
35136         this.data.each(fn, scope);
35137     },
35138
35139     /**
35140      * Gets all {@link Ext.data.Record records} modified since the last commit.  Modified records are
35141      * persisted across load operations (e.g., during paging). <b>Note</b>: deleted records are not
35142      * included.  See also <tt>{@link #pruneModifiedRecords}</tt> and
35143      * {@link Ext.data.Record}<tt>{@link Ext.data.Record#markDirty markDirty}.</tt>.
35144      * @return {Ext.data.Record[]} An array of {@link Ext.data.Record Records} containing outstanding
35145      * modifications.  To obtain modified fields within a modified record see
35146      *{@link Ext.data.Record}<tt>{@link Ext.data.Record#modified modified}.</tt>.
35147      */
35148     getModifiedRecords : function(){
35149         return this.modified;
35150     },
35151
35152     /**
35153      * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
35154      * and <tt>end</tt> and returns the result.
35155      * @param {String} property A field in each record
35156      * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
35157      * @param {Number} end (optional) The last record index to include (defaults to length - 1)
35158      * @return {Number} The sum
35159      */
35160     sum : function(property, start, end){
35161         var rs = this.data.items, v = 0;
35162         start = start || 0;
35163         end = (end || end === 0) ? end : rs.length-1;
35164
35165         for(var i = start; i <= end; i++){
35166             v += (rs[i].data[property] || 0);
35167         }
35168         return v;
35169     },
35170
35171     /**
35172      * @private
35173      * Returns a filter function used to test a the given property's value. Defers most of the work to
35174      * Ext.util.MixedCollection's createValueMatcher function
35175      * @param {String} property The property to create the filter function for
35176      * @param {String/RegExp} value The string/regex to compare the property value to
35177      * @param {Boolean} anyMatch True if we don't care if the filter value is not the full value (defaults to false)
35178      * @param {Boolean} caseSensitive True to create a case-sensitive regex (defaults to false)
35179      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
35180      */
35181     createFilterFn : function(property, value, anyMatch, caseSensitive, exactMatch){
35182         if(Ext.isEmpty(value, false)){
35183             return false;
35184         }
35185         value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
35186         return function(r) {
35187             return value.test(r.data[property]);
35188         };
35189     },
35190
35191     /**
35192      * Given an array of filter functions (each with optional scope), constructs and returns a single function that returns
35193      * the result of all of the filters ANDed together
35194      * @param {Array} filters The array of filter objects (each object should contain an 'fn' and optional scope)
35195      * @return {Function} The multiple filter function
35196      */
35197     createMultipleFilterFn: function(filters) {
35198         return function(record) {
35199             var isMatch = true;
35200
35201             for (var i=0, j = filters.length; i < j; i++) {
35202                 var filter = filters[i],
35203                     fn     = filter.fn,
35204                     scope  = filter.scope;
35205
35206                 isMatch = isMatch && fn.call(scope, record);
35207             }
35208
35209             return isMatch;
35210         };
35211     },
35212
35213     /**
35214      * Filter the {@link Ext.data.Record records} by a specified property. Alternatively, pass an array of filter
35215      * options to filter by more than one property.
35216      * Single filter example:
35217      * store.filter('name', 'Ed', true, true); //finds all records containing the substring 'Ed'
35218      * Multiple filter example:
35219      * store.filter([
35220      *   {
35221      *     property     : 'name',
35222      *     value        : 'Ed',
35223      *     anyMatch     : true, //optional, defaults to true
35224      *     caseSensitive: true  //optional, defaults to true
35225      *   },
35226      *
35227      *   //filter functions can also be passed
35228      *   {
35229      *     fn   : function(record) {
35230      *       return record.get('age') == 24
35231      *     },
35232      *     scope: this
35233      *   }
35234      * ]);
35235      * @param {String|Array} field A field on your records, or an array containing multiple filter options
35236      * @param {String/RegExp} value Either a string that the field should begin with, or a RegExp to test
35237      * against the field.
35238      * @param {Boolean} anyMatch (optional) <tt>true</tt> to match any part not just the beginning
35239      * @param {Boolean} caseSensitive (optional) <tt>true</tt> for case sensitive comparison
35240      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
35241      */
35242     filter : function(property, value, anyMatch, caseSensitive, exactMatch){
35243         //we can accept an array of filter objects, or a single filter object - normalize them here
35244         if (Ext.isObject(property)) {
35245             property = [property];
35246         }
35247
35248         if (Ext.isArray(property)) {
35249             var filters = [];
35250
35251             //normalize the filters passed into an array of filter functions
35252             for (var i=0, j = property.length; i < j; i++) {
35253                 var filter = property[i],
35254                     func   = filter.fn,
35255                     scope  = filter.scope || this;
35256
35257                 //if we weren't given a filter function, construct one now
35258                 if (!Ext.isFunction(func)) {
35259                     func = this.createFilterFn(filter.property, filter.value, filter.anyMatch, filter.caseSensitive, filter.exactMatch);
35260                 }
35261
35262                 filters.push({fn: func, scope: scope});
35263             }
35264
35265             var fn = this.createMultipleFilterFn(filters);
35266         } else {
35267             //classic single property filter
35268             var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
35269         }
35270
35271         return fn ? this.filterBy(fn) : this.clearFilter();
35272     },
35273
35274     /**
35275      * Filter by a function. The specified function will be called for each
35276      * Record in this Store. If the function returns <tt>true</tt> the Record is included,
35277      * otherwise it is filtered out.
35278      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
35279      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
35280      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
35281      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
35282      * </ul>
35283      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
35284      */
35285     filterBy : function(fn, scope){
35286         this.snapshot = this.snapshot || this.data;
35287         this.data = this.queryBy(fn, scope||this);
35288         this.fireEvent('datachanged', this);
35289     },
35290
35291     /**
35292      * Revert to a view of the Record cache with no filtering applied.
35293      * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
35294      * {@link #datachanged} event.
35295      */
35296     clearFilter : function(suppressEvent){
35297         if(this.isFiltered()){
35298             this.data = this.snapshot;
35299             delete this.snapshot;
35300             if(suppressEvent !== true){
35301                 this.fireEvent('datachanged', this);
35302             }
35303         }
35304     },
35305
35306     /**
35307      * Returns true if this store is currently filtered
35308      * @return {Boolean}
35309      */
35310     isFiltered : function(){
35311         return !!this.snapshot && this.snapshot != this.data;
35312     },
35313
35314     /**
35315      * Query the records by a specified property.
35316      * @param {String} field A field on your records
35317      * @param {String/RegExp} value Either a string that the field
35318      * should begin with, or a RegExp to test against the field.
35319      * @param {Boolean} anyMatch (optional) True to match any part not just the beginning
35320      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
35321      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
35322      */
35323     query : function(property, value, anyMatch, caseSensitive){
35324         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
35325         return fn ? this.queryBy(fn) : this.data.clone();
35326     },
35327
35328     /**
35329      * Query the cached records in this Store using a filtering function. The specified function
35330      * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
35331      * included in the results.
35332      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
35333      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
35334      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
35335      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
35336      * </ul>
35337      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
35338      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
35339      **/
35340     queryBy : function(fn, scope){
35341         var data = this.snapshot || this.data;
35342         return data.filterBy(fn, scope||this);
35343     },
35344
35345     /**
35346      * Finds the index of the first matching Record in this store by a specific field value.
35347      * @param {String} fieldName The name of the Record field to test.
35348      * @param {String/RegExp} value Either a string that the field value
35349      * should begin with, or a RegExp to test against the field.
35350      * @param {Number} startIndex (optional) The index to start searching at
35351      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
35352      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
35353      * @return {Number} The matched index or -1
35354      */
35355     find : function(property, value, start, anyMatch, caseSensitive){
35356         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
35357         return fn ? this.data.findIndexBy(fn, null, start) : -1;
35358     },
35359
35360     /**
35361      * Finds the index of the first matching Record in this store by a specific field value.
35362      * @param {String} fieldName The name of the Record field to test.
35363      * @param {Mixed} value The value to match the field against.
35364      * @param {Number} startIndex (optional) The index to start searching at
35365      * @return {Number} The matched index or -1
35366      */
35367     findExact: function(property, value, start){
35368         return this.data.findIndexBy(function(rec){
35369             return rec.get(property) === value;
35370         }, this, start);
35371     },
35372
35373     /**
35374      * Find the index of the first matching Record in this Store by a function.
35375      * If the function returns <tt>true</tt> it is considered a match.
35376      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
35377      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
35378      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
35379      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
35380      * </ul>
35381      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
35382      * @param {Number} startIndex (optional) The index to start searching at
35383      * @return {Number} The matched index or -1
35384      */
35385     findBy : function(fn, scope, start){
35386         return this.data.findIndexBy(fn, scope, start);
35387     },
35388
35389     /**
35390      * Collects unique values for a particular dataIndex from this store.
35391      * @param {String} dataIndex The property to collect
35392      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
35393      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
35394      * @return {Array} An array of the unique values
35395      **/
35396     collect : function(dataIndex, allowNull, bypassFilter){
35397         var d = (bypassFilter === true && this.snapshot) ?
35398                 this.snapshot.items : this.data.items;
35399         var v, sv, r = [], l = {};
35400         for(var i = 0, len = d.length; i < len; i++){
35401             v = d[i].data[dataIndex];
35402             sv = String(v);
35403             if((allowNull || !Ext.isEmpty(v)) && !l[sv]){
35404                 l[sv] = true;
35405                 r[r.length] = v;
35406             }
35407         }
35408         return r;
35409     },
35410
35411     // private
35412     afterEdit : function(record){
35413         if(this.modified.indexOf(record) == -1){
35414             this.modified.push(record);
35415         }
35416         this.fireEvent('update', this, record, Ext.data.Record.EDIT);
35417     },
35418
35419     // private
35420     afterReject : function(record){
35421         this.modified.remove(record);
35422         this.fireEvent('update', this, record, Ext.data.Record.REJECT);
35423     },
35424
35425     // private
35426     afterCommit : function(record){
35427         this.modified.remove(record);
35428         this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
35429     },
35430
35431     /**
35432      * Commit all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
35433      * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
35434      * Ext.data.Record.COMMIT.
35435      */
35436     commitChanges : function(){
35437         var m = this.modified.slice(0);
35438         this.modified = [];
35439         for(var i = 0, len = m.length; i < len; i++){
35440             m[i].commit();
35441         }
35442     },
35443
35444     /**
35445      * {@link Ext.data.Record#reject Reject} outstanding changes on all {@link #getModifiedRecords modified records}.
35446      */
35447     rejectChanges : function(){
35448         var m = this.modified.slice(0);
35449         this.modified = [];
35450         for(var i = 0, len = m.length; i < len; i++){
35451             m[i].reject();
35452         }
35453         var m = this.removed.slice(0).reverse();
35454         this.removed = [];
35455         for(var i = 0, len = m.length; i < len; i++){
35456             this.insert(m[i].lastIndex||0, m[i]);
35457             m[i].reject();
35458         }
35459     },
35460
35461     // private
35462     onMetaChange : function(meta){
35463         this.recordType = this.reader.recordType;
35464         this.fields = this.recordType.prototype.fields;
35465         delete this.snapshot;
35466         if(this.reader.meta.sortInfo){
35467             this.sortInfo = this.reader.meta.sortInfo;
35468         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
35469             delete this.sortInfo;
35470         }
35471         if(this.writer){
35472             this.writer.meta = this.reader.meta;
35473         }
35474         this.modified = [];
35475         this.fireEvent('metachange', this, this.reader.meta);
35476     },
35477
35478     // private
35479     findInsertIndex : function(record){
35480         this.suspendEvents();
35481         var data = this.data.clone();
35482         this.data.add(record);
35483         this.applySort();
35484         var index = this.data.indexOf(record);
35485         this.data = data;
35486         this.resumeEvents();
35487         return index;
35488     },
35489
35490     /**
35491      * Set the value for a property name in this store's {@link #baseParams}.  Usage:</p><pre><code>
35492 myStore.setBaseParam('foo', {bar:3});
35493 </code></pre>
35494      * @param {String} name Name of the property to assign
35495      * @param {Mixed} value Value to assign the <tt>name</tt>d property
35496      **/
35497     setBaseParam : function (name, value){
35498         this.baseParams = this.baseParams || {};
35499         this.baseParams[name] = value;
35500     }
35501 });
35502
35503 Ext.reg('store', Ext.data.Store);
35504
35505 /**
35506  * @class Ext.data.Store.Error
35507  * @extends Ext.Error
35508  * Store Error extension.
35509  * @param {String} name
35510  */
35511 Ext.data.Store.Error = Ext.extend(Ext.Error, {
35512     name: 'Ext.data.Store'
35513 });
35514 Ext.apply(Ext.data.Store.Error.prototype, {
35515     lang: {
35516         'writer-undefined' : 'Attempted to execute a write-action without a DataWriter installed.'
35517     }
35518 });
35519 /**
35520  * @class Ext.data.Field
35521  * <p>This class encapsulates the field definition information specified in the field definition objects
35522  * passed to {@link Ext.data.Record#create}.</p>
35523  * <p>Developers do not need to instantiate this class. Instances are created by {@link Ext.data.Record.create}
35524  * and cached in the {@link Ext.data.Record#fields fields} property of the created Record constructor's <b>prototype.</b></p>
35525  */
35526 Ext.data.Field = Ext.extend(Object, {
35527     
35528     constructor : function(config){
35529         if(Ext.isString(config)){
35530             config = {name: config};
35531         }
35532         Ext.apply(this, config);
35533         
35534         var types = Ext.data.Types,
35535             st = this.sortType,
35536             t;
35537
35538         if(this.type){
35539             if(Ext.isString(this.type)){
35540                 this.type = Ext.data.Types[this.type.toUpperCase()] || types.AUTO;
35541             }
35542         }else{
35543             this.type = types.AUTO;
35544         }
35545
35546         // named sortTypes are supported, here we look them up
35547         if(Ext.isString(st)){
35548             this.sortType = Ext.data.SortTypes[st];
35549         }else if(Ext.isEmpty(st)){
35550             this.sortType = this.type.sortType;
35551         }
35552
35553         if(!this.convert){
35554             this.convert = this.type.convert;
35555         }
35556     },
35557     
35558     /**
35559      * @cfg {String} name
35560      * The name by which the field is referenced within the Record. This is referenced by, for example,
35561      * the <code>dataIndex</code> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
35562      * <p>Note: In the simplest case, if no properties other than <code>name</code> are required, a field
35563      * definition may consist of just a String for the field name.</p>
35564      */
35565     /**
35566      * @cfg {Mixed} type
35567      * (Optional) The data type for automatic conversion from received data to the <i>stored</i> value if <code>{@link Ext.data.Field#convert convert}</code>
35568      * has not been specified. This may be specified as a string value. Possible values are
35569      * <div class="mdetail-params"><ul>
35570      * <li>auto (Default, implies no conversion)</li>
35571      * <li>string</li>
35572      * <li>int</li>
35573      * <li>float</li>
35574      * <li>boolean</li>
35575      * <li>date</li></ul></div>
35576      * <p>This may also be specified by referencing a member of the {@link Ext.data.Types} class.</p>
35577      * <p>Developers may create their own application-specific data types by defining new members of the
35578      * {@link Ext.data.Types} class.</p>
35579      */
35580     /**
35581      * @cfg {Function} convert
35582      * (Optional) A function which converts the value provided by the Reader into an object that will be stored
35583      * in the Record. It is passed the following parameters:<div class="mdetail-params"><ul>
35584      * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
35585      * the configured <code>{@link Ext.data.Field#defaultValue defaultValue}</code>.</div></li>
35586      * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
35587      * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
35588      *  ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
35589      * </ul></div>
35590      * <pre><code>
35591 // example of convert function
35592 function fullName(v, record){
35593     return record.name.last + ', ' + record.name.first;
35594 }
35595
35596 function location(v, record){
35597     return !record.city ? '' : (record.city + ', ' + record.state);
35598 }
35599
35600 var Dude = Ext.data.Record.create([
35601     {name: 'fullname',  convert: fullName},
35602     {name: 'firstname', mapping: 'name.first'},
35603     {name: 'lastname',  mapping: 'name.last'},
35604     {name: 'city', defaultValue: 'homeless'},
35605     'state',
35606     {name: 'location',  convert: location}
35607 ]);
35608
35609 // create the data store
35610 var store = new Ext.data.Store({
35611     reader: new Ext.data.JsonReader(
35612         {
35613             idProperty: 'key',
35614             root: 'daRoot',
35615             totalProperty: 'total'
35616         },
35617         Dude  // recordType
35618     )
35619 });
35620
35621 var myData = [
35622     { key: 1,
35623       name: { first: 'Fat',    last:  'Albert' }
35624       // notice no city, state provided in data object
35625     },
35626     { key: 2,
35627       name: { first: 'Barney', last:  'Rubble' },
35628       city: 'Bedrock', state: 'Stoneridge'
35629     },
35630     { key: 3,
35631       name: { first: 'Cliff',  last:  'Claven' },
35632       city: 'Boston',  state: 'MA'
35633     }
35634 ];
35635      * </code></pre>
35636      */
35637     /**
35638      * @cfg {String} dateFormat
35639      * <p>(Optional) Used when converting received data into a Date when the {@link #type} is specified as <code>"date"</code>.</p>
35640      * <p>A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
35641      * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
35642      * javascript millisecond timestamp. See {@link Date}</p>
35643      */
35644     dateFormat: null,
35645     /**
35646      * @cfg {Mixed} defaultValue
35647      * (Optional) The default value used <b>when a Record is being created by a {@link Ext.data.Reader Reader}</b>
35648      * when the item referenced by the <code>{@link Ext.data.Field#mapping mapping}</code> does not exist in the data
35649      * object (i.e. undefined). (defaults to "")
35650      */
35651     defaultValue: "",
35652     /**
35653      * @cfg {String/Number} mapping
35654      * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
35655      * that is creating the {@link Ext.data.Record Record} to extract the Field value from the data object.
35656      * If the path expression is the same as the field name, the mapping may be omitted.</p>
35657      * <p>The form of the mapping expression depends on the Reader being used.</p>
35658      * <div class="mdetail-params"><ul>
35659      * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
35660      * expression to reference the data from an element of the data item's {@link Ext.data.JsonReader#root root} Array. Defaults to the field name.</div></li>
35661      * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
35662      * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
35663      * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
35664      * of the field's value. Defaults to the field specification's Array position.</div></li>
35665      * </ul></div>
35666      * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
35667      * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
35668      * return the desired data.</p>
35669      */
35670     mapping: null,
35671     /**
35672      * @cfg {Function} sortType
35673      * (Optional) A function which converts a Field's value to a comparable value in order to ensure
35674      * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
35675      * sort example:<pre><code>
35676 // current sort     after sort we want
35677 // +-+------+          +-+------+
35678 // |1|First |          |1|First |
35679 // |2|Last  |          |3|Second|
35680 // |3|Second|          |2|Last  |
35681 // +-+------+          +-+------+
35682
35683 sortType: function(value) {
35684    switch (value.toLowerCase()) // native toLowerCase():
35685    {
35686       case 'first': return 1;
35687       case 'second': return 2;
35688       default: return 3;
35689    }
35690 }
35691      * </code></pre>
35692      */
35693     sortType : null,
35694     /**
35695      * @cfg {String} sortDir
35696      * (Optional) Initial direction to sort (<code>"ASC"</code> or  <code>"DESC"</code>).  Defaults to
35697      * <code>"ASC"</code>.
35698      */
35699     sortDir : "ASC",
35700     /**
35701      * @cfg {Boolean} allowBlank
35702      * (Optional) Used for validating a {@link Ext.data.Record record}, defaults to <code>true</code>.
35703      * An empty value here will cause {@link Ext.data.Record}.{@link Ext.data.Record#isValid isValid}
35704      * to evaluate to <code>false</code>.
35705      */
35706     allowBlank : true
35707 });
35708 /**
35709  * @class Ext.data.DataReader
35710  * Abstract base class for reading structured data from a data source and converting
35711  * it into an object containing {@link Ext.data.Record} objects and metadata for use
35712  * by an {@link Ext.data.Store}.  This class is intended to be extended and should not
35713  * be created directly. For existing implementations, see {@link Ext.data.ArrayReader},
35714  * {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}.
35715  * @constructor Create a new DataReader
35716  * @param {Object} meta Metadata configuration options (implementation-specific).
35717  * @param {Array/Object} recordType
35718  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
35719  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
35720  * constructor created using {@link Ext.data.Record#create}.</p>
35721  */
35722 Ext.data.DataReader = function(meta, recordType){
35723     /**
35724      * This DataReader's configured metadata as passed to the constructor.
35725      * @type Mixed
35726      * @property meta
35727      */
35728     this.meta = meta;
35729     /**
35730      * @cfg {Array/Object} fields
35731      * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
35732      * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
35733      * constructor created from {@link Ext.data.Record#create}.</p>
35734      */
35735     this.recordType = Ext.isArray(recordType) ?
35736         Ext.data.Record.create(recordType) : recordType;
35737
35738     // if recordType defined make sure extraction functions are defined
35739     if (this.recordType){
35740         this.buildExtractors();
35741     }
35742 };
35743
35744 Ext.data.DataReader.prototype = {
35745     /**
35746      * @cfg {String} messageProperty [undefined] Optional name of a property within a server-response that represents a user-feedback message.
35747      */
35748     /**
35749      * Abstract method created in extension's buildExtractors impl.
35750      */
35751     getTotal: Ext.emptyFn,
35752     /**
35753      * Abstract method created in extension's buildExtractors impl.
35754      */
35755     getRoot: Ext.emptyFn,
35756     /**
35757      * Abstract method created in extension's buildExtractors impl.
35758      */
35759     getMessage: Ext.emptyFn,
35760     /**
35761      * Abstract method created in extension's buildExtractors impl.
35762      */
35763     getSuccess: Ext.emptyFn,
35764     /**
35765      * Abstract method created in extension's buildExtractors impl.
35766      */
35767     getId: Ext.emptyFn,
35768     /**
35769      * Abstract method, overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}
35770      */
35771     buildExtractors : Ext.emptyFn,
35772     /**
35773      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}
35774      */
35775     extractValues : Ext.emptyFn,
35776
35777     /**
35778      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.
35779      * You <b>must</b> return at least the database pk using the idProperty defined in your DataReader configuration.  The incoming
35780      * data from server will be merged with the data in the local record.
35781      * In addition, you <b>must</b> return record-data from the server in the same order received.
35782      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed.
35783      * @param {Record/Record[]} record The phantom record to be realized.
35784      * @param {Object/Object[]} data The new record data to apply.  Must include the primary-key from database defined in idProperty field.
35785      */
35786     realize: function(rs, data){
35787         if (Ext.isArray(rs)) {
35788             for (var i = rs.length - 1; i >= 0; i--) {
35789                 // recurse
35790                 if (Ext.isArray(data)) {
35791                     this.realize(rs.splice(i,1).shift(), data.splice(i,1).shift());
35792                 }
35793                 else {
35794                     // weird...rs is an array but data isn't??  recurse but just send in the whole invalid data object.
35795                     // the else clause below will detect !this.isData and throw exception.
35796                     this.realize(rs.splice(i,1).shift(), data);
35797                 }
35798             }
35799         }
35800         else {
35801             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.
35802             if (Ext.isArray(data) && data.length == 1) {
35803                 data = data.shift();
35804             }
35805             if (!this.isData(data)) {
35806                 // TODO: Let exception-handler choose to commit or not rather than blindly rs.commit() here.
35807                 //rs.commit();
35808                 throw new Ext.data.DataReader.Error('realize', rs);
35809             }
35810             rs.phantom = false; // <-- That's what it's all about
35811             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords
35812             rs.id = this.getId(data);
35813             rs.data = data;
35814
35815             rs.commit();
35816         }
35817     },
35818
35819     /**
35820      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.
35821      * If returning data from multiple-records after a batch-update, you <b>must</b> return record-data from the server in
35822      * the same order received.  Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be
35823      * suppressed as the record receives fresh new data-hash
35824      * @param {Record/Record[]} rs
35825      * @param {Object/Object[]} data
35826      */
35827     update : function(rs, data) {
35828         if (Ext.isArray(rs)) {
35829             for (var i=rs.length-1; i >= 0; i--) {
35830                 if (Ext.isArray(data)) {
35831                     this.update(rs.splice(i,1).shift(), data.splice(i,1).shift());
35832                 }
35833                 else {
35834                     // weird...rs is an array but data isn't??  recurse but just send in the whole data object.
35835                     // the else clause below will detect !this.isData and throw exception.
35836                     this.update(rs.splice(i,1).shift(), data);
35837                 }
35838             }
35839         }
35840         else {
35841             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.
35842             if (Ext.isArray(data) && data.length == 1) {
35843                 data = data.shift();
35844             }
35845             if (this.isData(data)) {
35846                 rs.data = Ext.apply(rs.data, data);
35847             }
35848             rs.commit();
35849         }
35850     },
35851
35852     /**
35853      * returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
35854      * @param {Object[]/Object} data-root from server response
35855      * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record
35856      * @private
35857      */
35858     extractData : function(root, returnRecords) {
35859         // A bit ugly this, too bad the Record's raw data couldn't be saved in a common property named "raw" or something.
35860         var rawName = (this instanceof Ext.data.JsonReader) ? 'json' : 'node';
35861
35862         var rs = [];
35863
35864         // Had to add Check for XmlReader, #isData returns true if root is an Xml-object.  Want to check in order to re-factor
35865         // #extractData into DataReader base, since the implementations are almost identical for JsonReader, XmlReader
35866         if (this.isData(root) && !(this instanceof Ext.data.XmlReader)) {
35867             root = [root];
35868         }
35869         var f       = this.recordType.prototype.fields,
35870             fi      = f.items,
35871             fl      = f.length,
35872             rs      = [];
35873         if (returnRecords === true) {
35874             var Record = this.recordType;
35875             for (var i = 0; i < root.length; i++) {
35876                 var n = root[i];
35877                 var record = new Record(this.extractValues(n, fi, fl), this.getId(n));
35878                 record[rawName] = n;    // <-- There's implementation of ugly bit, setting the raw record-data.
35879                 rs.push(record);
35880             }
35881         }
35882         else {
35883             for (var i = 0; i < root.length; i++) {
35884                 var data = this.extractValues(root[i], fi, fl);
35885                 data[this.meta.idProperty] = this.getId(root[i]);
35886                 rs.push(data);
35887             }
35888         }
35889         return rs;
35890     },
35891
35892     /**
35893      * Returns true if the supplied data-hash <b>looks</b> and quacks like data.  Checks to see if it has a key
35894      * corresponding to idProperty defined in your DataReader config containing non-empty pk.
35895      * @param {Object} data
35896      * @return {Boolean}
35897      */
35898     isData : function(data) {
35899         return (data && Ext.isObject(data) && !Ext.isEmpty(this.getId(data))) ? true : false;
35900     },
35901
35902     // private function a store will createSequence upon
35903     onMetaChange : function(meta){
35904         delete this.ef;
35905         this.meta = meta;
35906         this.recordType = Ext.data.Record.create(meta.fields);
35907         this.buildExtractors();
35908     }
35909 };
35910
35911 /**
35912  * @class Ext.data.DataReader.Error
35913  * @extends Ext.Error
35914  * General error class for Ext.data.DataReader
35915  */
35916 Ext.data.DataReader.Error = Ext.extend(Ext.Error, {
35917     constructor : function(message, arg) {
35918         this.arg = arg;
35919         Ext.Error.call(this, message);
35920     },
35921     name: 'Ext.data.DataReader'
35922 });
35923 Ext.apply(Ext.data.DataReader.Error.prototype, {
35924     lang : {
35925         'update': "#update received invalid data from server.  Please see docs for DataReader#update and review your DataReader configuration.",
35926         'realize': "#realize was called with invalid remote-data.  Please see the docs for DataReader#realize and review your DataReader configuration.",
35927         'invalid-response': "#readResponse received an invalid response from the server."
35928     }
35929 });
35930 /**
35931  * @class Ext.data.DataWriter
35932  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
35933  * an Ext.data.Store and a server-side framework. A Writer enabled Store will
35934  * automatically manage the Ajax requests to perform CRUD actions on a Store.</p>
35935  * <p>Ext.data.DataWriter is an abstract base class which is intended to be extended
35936  * and should not be created directly. For existing implementations, see
35937  * {@link Ext.data.JsonWriter}.</p>
35938  * <p>Creating a writer is simple:</p>
35939  * <pre><code>
35940 var writer = new Ext.data.JsonWriter({
35941     encode: false   // &lt;--- false causes data to be printed to jsonData config-property of Ext.Ajax#reqeust
35942 });
35943  * </code></pre>
35944  * * <p>Same old JsonReader as Ext-2.x:</p>
35945  * <pre><code>
35946 var reader = new Ext.data.JsonReader({idProperty: 'id'}, [{name: 'first'}, {name: 'last'}, {name: 'email'}]);
35947  * </code></pre>
35948  *
35949  * <p>The proxy for a writer enabled store can be configured with a simple <code>url</code>:</p>
35950  * <pre><code>
35951 // Create a standard HttpProxy instance.
35952 var proxy = new Ext.data.HttpProxy({
35953     url: 'app.php/users'    // &lt;--- Supports "provides"-type urls, such as '/users.json', '/products.xml' (Hello Rails/Merb)
35954 });
35955  * </code></pre>
35956  * <p>For finer grained control, the proxy may also be configured with an <code>API</code>:</p>
35957  * <pre><code>
35958 // Maximum flexibility with the API-configuration
35959 var proxy = new Ext.data.HttpProxy({
35960     api: {
35961         read    : 'app.php/users/read',
35962         create  : 'app.php/users/create',
35963         update  : 'app.php/users/update',
35964         destroy : {  // &lt;--- Supports object-syntax as well
35965             url: 'app.php/users/destroy',
35966             method: "DELETE"
35967         }
35968     }
35969 });
35970  * </code></pre>
35971  * <p>Pulling it all together into a Writer-enabled Store:</p>
35972  * <pre><code>
35973 var store = new Ext.data.Store({
35974     proxy: proxy,
35975     reader: reader,
35976     writer: writer,
35977     autoLoad: true,
35978     autoSave: true  // -- Cell-level updates.
35979 });
35980  * </code></pre>
35981  * <p>Initiating write-actions <b>automatically</b>, using the existing Ext2.0 Store/Record API:</p>
35982  * <pre><code>
35983 var rec = store.getAt(0);
35984 rec.set('email', 'foo@bar.com');  // &lt;--- Immediately initiates an UPDATE action through configured proxy.
35985
35986 store.remove(rec);  // &lt;---- Immediately initiates a DESTROY action through configured proxy.
35987  * </code></pre>
35988  * <p>For <b>record/batch</b> updates, use the Store-configuration {@link Ext.data.Store#autoSave autoSave:false}</p>
35989  * <pre><code>
35990 var store = new Ext.data.Store({
35991     proxy: proxy,
35992     reader: reader,
35993     writer: writer,
35994     autoLoad: true,
35995     autoSave: false  // -- disable cell-updates
35996 });
35997
35998 var urec = store.getAt(0);
35999 urec.set('email', 'foo@bar.com');
36000
36001 var drec = store.getAt(1);
36002 store.remove(drec);
36003
36004 // Push the button!
36005 store.save();
36006  * </code></pre>
36007  * @constructor Create a new DataWriter
36008  * @param {Object} meta Metadata configuration options (implementation-specific)
36009  * @param {Object} recordType Either an Array of field definition objects as specified
36010  * in {@link Ext.data.Record#create}, or an {@link Ext.data.Record} object created
36011  * using {@link Ext.data.Record#create}.
36012  */
36013 Ext.data.DataWriter = function(config){
36014     Ext.apply(this, config);
36015 };
36016 Ext.data.DataWriter.prototype = {
36017
36018     /**
36019      * @cfg {Boolean} writeAllFields
36020      * <tt>false</tt> by default.  Set <tt>true</tt> to have DataWriter return ALL fields of a modified
36021      * record -- not just those that changed.
36022      * <tt>false</tt> to have DataWriter only request modified fields from a record.
36023      */
36024     writeAllFields : false,
36025     /**
36026      * @cfg {Boolean} listful
36027      * <tt>false</tt> by default.  Set <tt>true</tt> to have the DataWriter <b>always</b> write HTTP params as a list,
36028      * even when acting upon a single record.
36029      */
36030     listful : false,    // <-- listful is actually not used internally here in DataWriter.  @see Ext.data.Store#execute.
36031
36032     /**
36033      * Compiles a Store recordset into a data-format defined by an extension such as {@link Ext.data.JsonWriter} or {@link Ext.data.XmlWriter} in preparation for a {@link Ext.data.Api#actions server-write action}.  The first two params are similar similar in nature to {@link Ext#apply},
36034      * Where the first parameter is the <i>receiver</i> of paramaters and the second, baseParams, <i>the source</i>.
36035      * @param {Object} params The request-params receiver.
36036      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
36037      * @param {String} action [{@link Ext.data.Api#actions create|update|destroy}]
36038      * @param {Record/Record[]} rs The recordset to write, the subject(s) of the write action.
36039      */
36040     apply : function(params, baseParams, action, rs) {
36041         var data    = [],
36042         renderer    = action + 'Record';
36043         // TODO implement @cfg listful here
36044         if (Ext.isArray(rs)) {
36045             Ext.each(rs, function(rec){
36046                 data.push(this[renderer](rec));
36047             }, this);
36048         }
36049         else if (rs instanceof Ext.data.Record) {
36050             data = this[renderer](rs);
36051         }
36052         this.render(params, baseParams, data);
36053     },
36054
36055     /**
36056      * abstract method meant to be overridden by all DataWriter extensions.  It's the extension's job to apply the "data" to the "params".
36057      * The data-object provided to render is populated with data according to the meta-info defined in the user's DataReader config,
36058      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36059      * @param {Record[]} rs Store recordset
36060      * @param {Object} params Http params to be sent to server.
36061      * @param {Object} data object populated according to DataReader meta-data.
36062      */
36063     render : Ext.emptyFn,
36064
36065     /**
36066      * @cfg {Function} updateRecord Abstract method that should be implemented in all subclasses
36067      * (e.g.: {@link Ext.data.JsonWriter#updateRecord JsonWriter.updateRecord}
36068      */
36069     updateRecord : Ext.emptyFn,
36070
36071     /**
36072      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
36073      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
36074      */
36075     createRecord : Ext.emptyFn,
36076
36077     /**
36078      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
36079      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
36080      */
36081     destroyRecord : Ext.emptyFn,
36082
36083     /**
36084      * Converts a Record to a hash, taking into account the state of the Ext.data.Record along with configuration properties
36085      * related to its rendering, such as {@link #writeAllFields}, {@link Ext.data.Record#phantom phantom}, {@link Ext.data.Record#getChanges getChanges} and
36086      * {@link Ext.data.DataReader#idProperty idProperty}
36087      * @param {Ext.data.Record} rec The Record from which to create a hash.
36088      * @param {Object} config <b>NOT YET IMPLEMENTED</b>.  Will implement an exlude/only configuration for fine-control over which fields do/don't get rendered.
36089      * @return {Object}
36090      * @protected
36091      * TODO Implement excludes/only configuration with 2nd param?
36092      */
36093     toHash : function(rec, config) {
36094         var map = rec.fields.map,
36095             data = {},
36096             raw = (this.writeAllFields === false && rec.phantom === false) ? rec.getChanges() : rec.data,
36097             m;
36098         Ext.iterate(raw, function(prop, value){
36099             if((m = map[prop])){
36100                 data[m.mapping ? m.mapping : m.name] = value;
36101             }
36102         });
36103         // we don't want to write Ext auto-generated id to hash.  Careful not to remove it on Models not having auto-increment pk though.
36104         // We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
36105         // we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
36106         if (rec.phantom) {
36107             if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
36108                 delete data[this.meta.idProperty];
36109             }
36110         } else {
36111             data[this.meta.idProperty] = rec.id
36112         }
36113         return data;
36114     },
36115
36116     /**
36117      * Converts a {@link Ext.data.DataWriter#toHash Hashed} {@link Ext.data.Record} to fields-array array suitable
36118      * for encoding to xml via XTemplate, eg:
36119 <code><pre>&lt;tpl for=".">&lt;{name}>{value}&lt;/{name}&lt;/tpl></pre></code>
36120      * eg, <b>non-phantom</b>:
36121 <code><pre>{id: 1, first: 'foo', last: 'bar'} --> [{name: 'id', value: 1}, {name: 'first', value: 'foo'}, {name: 'last', value: 'bar'}]</pre></code>
36122      * {@link Ext.data.Record#phantom Phantom} records will have had their idProperty omitted in {@link #toHash} if determined to be auto-generated.
36123      * Non AUTOINCREMENT pks should have been protected.
36124      * @param {Hash} data Hashed by Ext.data.DataWriter#toHash
36125      * @return {[Object]} Array of attribute-objects.
36126      * @protected
36127      */
36128     toArray : function(data) {
36129         var fields = [];
36130         Ext.iterate(data, function(k, v) {fields.push({name: k, value: v});},this);
36131         return fields;
36132     }
36133 };/**
36134  * @class Ext.data.DataProxy
36135  * @extends Ext.util.Observable
36136  * <p>Abstract base class for implementations which provide retrieval of unformatted data objects.
36137  * This class is intended to be extended and should not be created directly. For existing implementations,
36138  * see {@link Ext.data.DirectProxy}, {@link Ext.data.HttpProxy}, {@link Ext.data.ScriptTagProxy} and
36139  * {@link Ext.data.MemoryProxy}.</p>
36140  * <p>DataProxy implementations are usually used in conjunction with an implementation of {@link Ext.data.DataReader}
36141  * (of the appropriate type which knows how to parse the data object) to provide a block of
36142  * {@link Ext.data.Records} to an {@link Ext.data.Store}.</p>
36143  * <p>The parameter to a DataProxy constructor may be an {@link Ext.data.Connection} or can also be the
36144  * config object to an {@link Ext.data.Connection}.</p>
36145  * <p>Custom implementations must implement either the <code><b>doRequest</b></code> method (preferred) or the
36146  * <code>load</code> method (deprecated). See
36147  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#doRequest doRequest} or
36148  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#load load} for additional details.</p>
36149  * <p><b><u>Example 1</u></b></p>
36150  * <pre><code>
36151 proxy: new Ext.data.ScriptTagProxy({
36152     {@link Ext.data.Connection#url url}: 'http://extjs.com/forum/topics-remote.php'
36153 }),
36154  * </code></pre>
36155  * <p><b><u>Example 2</u></b></p>
36156  * <pre><code>
36157 proxy : new Ext.data.HttpProxy({
36158     {@link Ext.data.Connection#method method}: 'GET',
36159     {@link Ext.data.HttpProxy#prettyUrls prettyUrls}: false,
36160     {@link Ext.data.Connection#url url}: 'local/default.php', // see options parameter for {@link Ext.Ajax#request}
36161     {@link #api}: {
36162         // all actions except the following will use above url
36163         create  : 'local/new.php',
36164         update  : 'local/update.php'
36165     }
36166 }),
36167  * </code></pre>
36168  * <p>And <b>new in Ext version 3</b>, attach centralized event-listeners upon the DataProxy class itself!  This is a great place
36169  * to implement a <i>messaging system</i> to centralize your application's user-feedback and error-handling.</p>
36170  * <pre><code>
36171 // Listen to all "beforewrite" event fired by all proxies.
36172 Ext.data.DataProxy.on('beforewrite', function(proxy, action) {
36173     console.log('beforewrite: ', action);
36174 });
36175
36176 // Listen to "write" event fired by all proxies
36177 Ext.data.DataProxy.on('write', function(proxy, action, data, res, rs) {
36178     console.info('write: ', action);
36179 });
36180
36181 // Listen to "exception" event fired by all proxies
36182 Ext.data.DataProxy.on('exception', function(proxy, type, action) {
36183     console.error(type + action + ' exception);
36184 });
36185  * </code></pre>
36186  * <b>Note:</b> These three events are all fired with the signature of the corresponding <i>DataProxy instance</i> event {@link #beforewrite beforewrite}, {@link #write write} and {@link #exception exception}.
36187  */
36188 Ext.data.DataProxy = function(conn){
36189     // make sure we have a config object here to support ux proxies.
36190     // All proxies should now send config into superclass constructor.
36191     conn = conn || {};
36192
36193     // This line caused a bug when people use custom Connection object having its own request method.
36194     // http://extjs.com/forum/showthread.php?t=67194.  Have to set DataProxy config
36195     //Ext.applyIf(this, conn);
36196
36197     this.api     = conn.api;
36198     this.url     = conn.url;
36199     this.restful = conn.restful;
36200     this.listeners = conn.listeners;
36201
36202     // deprecated
36203     this.prettyUrls = conn.prettyUrls;
36204
36205     /**
36206      * @cfg {Object} api
36207      * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".
36208      * Defaults to:<pre><code>
36209 api: {
36210     read    : undefined,
36211     create  : undefined,
36212     update  : undefined,
36213     destroy : undefined
36214 }
36215      * </code></pre>
36216      * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>
36217      * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the
36218      * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>
36219      * <p>For example:</p>
36220      * <pre><code>
36221 api: {
36222     load :    '/controller/load',
36223     create :  '/controller/new',  // Server MUST return idProperty of new record
36224     save :    '/controller/update',
36225     destroy : '/controller/destroy_action'
36226 }
36227
36228 // Alternatively, one can use the object-form to specify each API-action
36229 api: {
36230     load: {url: 'read.php', method: 'GET'},
36231     create: 'create.php',
36232     destroy: 'destroy.php',
36233     save: 'update.php'
36234 }
36235      * </code></pre>
36236      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request
36237      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>
36238      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API
36239      * property should be modified before the action is requested using the corresponding before
36240      * action event.  For example to modify the URL associated with the load action:
36241      * <pre><code>
36242 // modify the url for the action
36243 myStore.on({
36244     beforeload: {
36245         fn: function (store, options) {
36246             // use <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> to change the URL for *just* this request.
36247             store.proxy.setUrl('changed1.php');
36248
36249             // set optional second parameter to true to make this URL change
36250             // permanent, applying this URL for all subsequent requests.
36251             store.proxy.setUrl('changed1.php', true);
36252
36253             // Altering the proxy API should be done using the public
36254             // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt>.
36255             store.proxy.setApi('read', 'changed2.php');
36256
36257             // Or set the entire API with a config-object.
36258             // When using the config-object option, you must redefine the <b>entire</b>
36259             // API -- not just a specific action of it.
36260             store.proxy.setApi({
36261                 read    : 'changed_read.php',
36262                 create  : 'changed_create.php',
36263                 update  : 'changed_update.php',
36264                 destroy : 'changed_destroy.php'
36265             });
36266         }
36267     }
36268 });
36269      * </code></pre>
36270      * </p>
36271      */
36272
36273     this.addEvents(
36274         /**
36275          * @event exception
36276          * <p>Fires if an exception occurs in the Proxy during a remote request. This event is relayed
36277          * through a corresponding {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},
36278          * so any Store instance may observe this event.</p>
36279          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
36280          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of exception events from <b>all</b>
36281          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>
36282          * <p>This event can be fired for one of two reasons:</p>
36283          * <div class="mdetail-params"><ul>
36284          * <li>remote-request <b>failed</b> : <div class="sub-desc">
36285          * The server did not return status === 200.
36286          * </div></li>
36287          * <li>remote-request <b>succeeded</b> : <div class="sub-desc">
36288          * The remote-request succeeded but the reader could not read the response.
36289          * This means the server returned data, but the configured Reader threw an
36290          * error while reading the response.  In this case, this event will be
36291          * raised and the caught error will be passed along into this event.
36292          * </div></li>
36293          * </ul></div>
36294          * <br><p>This event fires with two different contexts based upon the 2nd
36295          * parameter <tt>type [remote|response]</tt>.  The first four parameters
36296          * are identical between the two contexts -- only the final two parameters
36297          * differ.</p>
36298          * @param {DataProxy} this The proxy that sent the request
36299          * @param {String} type
36300          * <p>The value of this parameter will be either <tt>'response'</tt> or <tt>'remote'</tt>.</p>
36301          * <div class="mdetail-params"><ul>
36302          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">
36303          * <p>An <b>invalid</b> response from the server was returned: either 404,
36304          * 500 or the response meta-data does not match that defined in the DataReader
36305          * (e.g.: root, idProperty, successProperty).</p>
36306          * </div></li>
36307          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">
36308          * <p>A <b>valid</b> response was returned from the server having
36309          * successProperty === false.  This response might contain an error-message
36310          * sent from the server.  For example, the user may have failed
36311          * authentication/authorization or a database validation error occurred.</p>
36312          * </div></li>
36313          * </ul></div>
36314          * @param {String} action Name of the action (see {@link Ext.data.Api#actions}.
36315          * @param {Object} options The options for the action that were specified in the {@link #request}.
36316          * @param {Object} response
36317          * <p>The value of this parameter depends on the value of the <code>type</code> parameter:</p>
36318          * <div class="mdetail-params"><ul>
36319          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">
36320          * <p>The raw browser response object (e.g.: XMLHttpRequest)</p>
36321          * </div></li>
36322          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">
36323          * <p>The decoded response object sent from the server.</p>
36324          * </div></li>
36325          * </ul></div>
36326          * @param {Mixed} arg
36327          * <p>The type and value of this parameter depends on the value of the <code>type</code> parameter:</p>
36328          * <div class="mdetail-params"><ul>
36329          * <li><b><tt>'response'</tt></b> : Error<div class="sub-desc">
36330          * <p>The JavaScript Error object caught if the configured Reader could not read the data.
36331          * If the remote request returns success===false, this parameter will be null.</p>
36332          * </div></li>
36333          * <li><b><tt>'remote'</tt></b> : Record/Record[]<div class="sub-desc">
36334          * <p>This parameter will only exist if the <tt>action</tt> was a <b>write</b> action
36335          * (Ext.data.Api.actions.create|update|destroy).</p>
36336          * </div></li>
36337          * </ul></div>
36338          */
36339         'exception',
36340         /**
36341          * @event beforeload
36342          * Fires before a request to retrieve a data object.
36343          * @param {DataProxy} this The proxy for the request
36344          * @param {Object} params The params object passed to the {@link #request} function
36345          */
36346         'beforeload',
36347         /**
36348          * @event load
36349          * Fires before the load method's callback is called.
36350          * @param {DataProxy} this The proxy for the request
36351          * @param {Object} o The request transaction object
36352          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function
36353          */
36354         'load',
36355         /**
36356          * @event loadexception
36357          * <p>This event is <b>deprecated</b>.  The signature of the loadexception event
36358          * varies depending on the proxy, use the catch-all {@link #exception} event instead.
36359          * This event will fire in addition to the {@link #exception} event.</p>
36360          * @param {misc} misc See {@link #exception}.
36361          * @deprecated
36362          */
36363         'loadexception',
36364         /**
36365          * @event beforewrite
36366          * <p>Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy</p>
36367          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
36368          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of beforewrite events from <b>all</b>
36369          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>
36370          * @param {DataProxy} this The proxy for the request
36371          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
36372          * @param {Record/Record[]} rs The Record(s) to create|update|destroy.
36373          * @param {Object} params The request <code>params</code> object.  Edit <code>params</code> to add parameters to the request.
36374          */
36375         'beforewrite',
36376         /**
36377          * @event write
36378          * <p>Fires before the request-callback is called</p>
36379          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
36380          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of write events from <b>all</b>
36381          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>
36382          * @param {DataProxy} this The proxy that sent the request
36383          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]
36384          * @param {Object} data The data object extracted from the server-response
36385          * @param {Object} response The decoded response from server
36386          * @param {Record/Record[]} rs The Record(s) from Store
36387          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function
36388          */
36389         'write'
36390     );
36391     Ext.data.DataProxy.superclass.constructor.call(this);
36392
36393     // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.
36394     try {
36395         Ext.data.Api.prepare(this);
36396     } catch (e) {
36397         if (e instanceof Ext.data.Api.Error) {
36398             e.toConsole();
36399         }
36400     }
36401     // relay each proxy's events onto Ext.data.DataProxy class for centralized Proxy-listening
36402     Ext.data.DataProxy.relayEvents(this, ['beforewrite', 'write', 'exception']);
36403 };
36404
36405 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {
36406     /**
36407      * @cfg {Boolean} restful
36408      * <p>Defaults to <tt>false</tt>.  Set to <tt>true</tt> to operate in a RESTful manner.</p>
36409      * <br><p> Note: this parameter will automatically be set to <tt>true</tt> if the
36410      * {@link Ext.data.Store} it is plugged into is set to <code>restful: true</code>. If the
36411      * Store is RESTful, there is no need to set this option on the proxy.</p>
36412      * <br><p>RESTful implementations enable the serverside framework to automatically route
36413      * actions sent to one url based upon the HTTP method, for example:
36414      * <pre><code>
36415 store: new Ext.data.Store({
36416     restful: true,
36417     proxy: new Ext.data.HttpProxy({url:'/users'}); // all requests sent to /users
36418     ...
36419 )}
36420      * </code></pre>
36421      * If there is no <code>{@link #api}</code> specified in the configuration of the proxy,
36422      * all requests will be marshalled to a single RESTful url (/users) so the serverside
36423      * framework can inspect the HTTP Method and act accordingly:
36424      * <pre>
36425 <u>Method</u>   <u>url</u>        <u>action</u>
36426 POST     /users     create
36427 GET      /users     read
36428 PUT      /users/23  update
36429 DESTROY  /users/23  delete
36430      * </pre></p>
36431      * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's
36432      * {@link Ext.data.Record#id id} will be appended to the url. Some MVC (e.g., Ruby on Rails,
36433      * Merb and Django) support segment based urls where the segments in the URL follow the
36434      * Model-View-Controller approach:<pre><code>
36435      * someSite.com/controller/action/id
36436      * </code></pre>
36437      * Where the segments in the url are typically:<div class="mdetail-params"><ul>
36438      * <li>The first segment : represents the controller class that should be invoked.</li>
36439      * <li>The second segment : represents the class function, or method, that should be called.</li>
36440      * <li>The third segment : represents the ID (a variable typically passed to the method).</li>
36441      * </ul></div></p>
36442      * <br><p>Refer to <code>{@link Ext.data.DataProxy#api}</code> for additional information.</p>
36443      */
36444     restful: false,
36445
36446     /**
36447      * <p>Redefines the Proxy's API or a single action of an API. Can be called with two method signatures.</p>
36448      * <p>If called with an object as the only parameter, the object should redefine the <b>entire</b> API, e.g.:</p><pre><code>
36449 proxy.setApi({
36450     read    : '/users/read',
36451     create  : '/users/create',
36452     update  : '/users/update',
36453     destroy : '/users/destroy'
36454 });
36455 </code></pre>
36456      * <p>If called with two parameters, the first parameter should be a string specifying the API action to
36457      * redefine and the second parameter should be the URL (or function if using DirectProxy) to call for that action, e.g.:</p><pre><code>
36458 proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');
36459 </code></pre>
36460      * @param {String/Object} api An API specification object, or the name of an action.
36461      * @param {String/Function} url The URL (or function if using DirectProxy) to call for the action.
36462      */
36463     setApi : function() {
36464         if (arguments.length == 1) {
36465             var valid = Ext.data.Api.isValid(arguments[0]);
36466             if (valid === true) {
36467                 this.api = arguments[0];
36468             }
36469             else {
36470                 throw new Ext.data.Api.Error('invalid', valid);
36471             }
36472         }
36473         else if (arguments.length == 2) {
36474             if (!Ext.data.Api.isAction(arguments[0])) {
36475                 throw new Ext.data.Api.Error('invalid', arguments[0]);
36476             }
36477             this.api[arguments[0]] = arguments[1];
36478         }
36479         Ext.data.Api.prepare(this);
36480     },
36481
36482     /**
36483      * Returns true if the specified action is defined as a unique action in the api-config.
36484      * request.  If all API-actions are routed to unique urls, the xaction parameter is unecessary.  However, if no api is defined
36485      * and all Proxy actions are routed to DataProxy#url, the server-side will require the xaction parameter to perform a switch to
36486      * the corresponding code for CRUD action.
36487      * @param {String [Ext.data.Api.CREATE|READ|UPDATE|DESTROY]} action
36488      * @return {Boolean}
36489      */
36490     isApiAction : function(action) {
36491         return (this.api[action]) ? true : false;
36492     },
36493
36494     /**
36495      * All proxy actions are executed through this method.  Automatically fires the "before" + action event
36496      * @param {String} action Name of the action
36497      * @param {Ext.data.Record/Ext.data.Record[]/null} rs Will be null when action is 'load'
36498      * @param {Object} params
36499      * @param {Ext.data.DataReader} reader
36500      * @param {Function} callback
36501      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the Proxy object.
36502      * @param {Object} options Any options specified for the action (e.g. see {@link Ext.data.Store#load}.
36503      */
36504     request : function(action, rs, params, reader, callback, scope, options) {
36505         if (!this.api[action] && !this.load) {
36506             throw new Ext.data.DataProxy.Error('action-undefined', action);
36507         }
36508         params = params || {};
36509         if ((action === Ext.data.Api.actions.read) ? this.fireEvent("beforeload", this, params) : this.fireEvent("beforewrite", this, action, rs, params) !== false) {
36510             this.doRequest.apply(this, arguments);
36511         }
36512         else {
36513             callback.call(scope || this, null, options, false);
36514         }
36515     },
36516
36517
36518     /**
36519      * <b>Deprecated</b> load method using old method signature. See {@doRequest} for preferred method.
36520      * @deprecated
36521      * @param {Object} params
36522      * @param {Object} reader
36523      * @param {Object} callback
36524      * @param {Object} scope
36525      * @param {Object} arg
36526      */
36527     load : null,
36528
36529     /**
36530      * @cfg {Function} doRequest Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.
36531      * (e.g.: {@link Ext.data.HttpProxy#doRequest HttpProxy.doRequest},
36532      * {@link Ext.data.DirectProxy#doRequest DirectProxy.doRequest}).
36533      */
36534     doRequest : function(action, rs, params, reader, callback, scope, options) {
36535         // default implementation of doRequest for backwards compatibility with 2.0 proxies.
36536         // If we're executing here, the action is probably "load".
36537         // Call with the pre-3.0 method signature.
36538         this.load(params, reader, callback, scope, options);
36539     },
36540
36541     /**
36542      * @cfg {Function} onRead Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.  Callback for read {@link Ext.data.Api#actions action}.
36543      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
36544      * @param {Object} o The request transaction object
36545      * @param {Object} res The server response
36546      * @fires loadexception (deprecated)
36547      * @fires exception
36548      * @fires load
36549      * @protected
36550      */
36551     onRead : Ext.emptyFn,
36552     /**
36553      * @cfg {Function} onWrite Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.  Callback for <i>create, update and destroy</i> {@link Ext.data.Api#actions actions}.
36554      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36555      * @param {Object} trans The request transaction object
36556      * @param {Object} res The server response
36557      * @fires exception
36558      * @fires write
36559      * @protected
36560      */
36561     onWrite : Ext.emptyFn,
36562     /**
36563      * buildUrl
36564      * Sets the appropriate url based upon the action being executed.  If restful is true, and only a single record is being acted upon,
36565      * url will be built Rails-style, as in "/controller/action/32".  restful will aply iff the supplied record is an
36566      * instance of Ext.data.Record rather than an Array of them.
36567      * @param {String} action The api action being executed [read|create|update|destroy]
36568      * @param {Ext.data.Record/Ext.data.Record[]} record The record or Array of Records being acted upon.
36569      * @return {String} url
36570      * @private
36571      */
36572     buildUrl : function(action, record) {
36573         record = record || null;
36574
36575         // conn.url gets nullified after each request.  If it's NOT null here, that means the user must have intervened with a call
36576         // to DataProxy#setUrl or DataProxy#setApi and changed it before the request was executed.  If that's the case, use conn.url,
36577         // otherwise, build the url from the api or this.url.
36578         var url = (this.conn && this.conn.url) ? this.conn.url : (this.api[action]) ? this.api[action].url : this.url;
36579         if (!url) {
36580             throw new Ext.data.Api.Error('invalid-url', action);
36581         }
36582
36583         // look for urls having "provides" suffix used in some MVC frameworks like Rails/Merb and others.  The provides suffice informs
36584         // the server what data-format the client is dealing with and returns data in the same format (eg: application/json, application/xml, etc)
36585         // e.g.: /users.json, /users.xml, etc.
36586         // with restful routes, we need urls like:
36587         // PUT /users/1.json
36588         // DELETE /users/1.json
36589         var provides = null;
36590         var m = url.match(/(.*)(\.json|\.xml|\.html)$/);
36591         if (m) {
36592             provides = m[2];    // eg ".json"
36593             url      = m[1];    // eg: "/users"
36594         }
36595         // prettyUrls is deprectated in favor of restful-config
36596         if ((this.restful === true || this.prettyUrls === true) && record instanceof Ext.data.Record && !record.phantom) {
36597             url += '/' + record.id;
36598         }
36599         return (provides === null) ? url : url + provides;
36600     },
36601
36602     /**
36603      * Destroys the proxy by purging any event listeners and cancelling any active requests.
36604      */
36605     destroy: function(){
36606         this.purgeListeners();
36607     }
36608 });
36609
36610 // Apply the Observable prototype to the DataProxy class so that proxy instances can relay their
36611 // events to the class.  Allows for centralized listening of all proxy instances upon the DataProxy class.
36612 Ext.apply(Ext.data.DataProxy, Ext.util.Observable.prototype);
36613 Ext.util.Observable.call(Ext.data.DataProxy);
36614
36615 /**
36616  * @class Ext.data.DataProxy.Error
36617  * @extends Ext.Error
36618  * DataProxy Error extension.
36619  * constructor
36620  * @param {String} message Message describing the error.
36621  * @param {Record/Record[]} arg
36622  */
36623 Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {
36624     constructor : function(message, arg) {
36625         this.arg = arg;
36626         Ext.Error.call(this, message);
36627     },
36628     name: 'Ext.data.DataProxy'
36629 });
36630 Ext.apply(Ext.data.DataProxy.Error.prototype, {
36631     lang: {
36632         'action-undefined': "DataProxy attempted to execute an API-action but found an undefined url / function.  Please review your Proxy url/api-configuration.",
36633         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'
36634     }
36635 });
36636
36637
36638 /**
36639  * @class Ext.data.Request
36640  * A simple Request class used internally to the data package to provide more generalized remote-requests
36641  * to a DataProxy.
36642  * TODO Not yet implemented.  Implement in Ext.data.Store#execute
36643  */
36644 Ext.data.Request = function(params) {
36645     Ext.apply(this, params);
36646 };
36647 Ext.data.Request.prototype = {
36648     /**
36649      * @cfg {String} action
36650      */
36651     action : undefined,
36652     /**
36653      * @cfg {Ext.data.Record[]/Ext.data.Record} rs The Store recordset associated with the request.
36654      */
36655     rs : undefined,
36656     /**
36657      * @cfg {Object} params HTTP request params
36658      */
36659     params: undefined,
36660     /**
36661      * @cfg {Function} callback The function to call when request is complete
36662      */
36663     callback : Ext.emptyFn,
36664     /**
36665      * @cfg {Object} scope The scope of the callback funtion
36666      */
36667     scope : undefined,
36668     /**
36669      * @cfg {Ext.data.DataReader} reader The DataReader instance which will parse the received response
36670      */
36671     reader : undefined
36672 };
36673 /**
36674  * @class Ext.data.Response
36675  * A generic response class to normalize response-handling internally to the framework.
36676  */
36677 Ext.data.Response = function(params) {
36678     Ext.apply(this, params);
36679 };
36680 Ext.data.Response.prototype = {
36681     /**
36682      * @cfg {String} action {@link Ext.data.Api#actions}
36683      */
36684     action: undefined,
36685     /**
36686      * @cfg {Boolean} success
36687      */
36688     success : undefined,
36689     /**
36690      * @cfg {String} message
36691      */
36692     message : undefined,
36693     /**
36694      * @cfg {Array/Object} data
36695      */
36696     data: undefined,
36697     /**
36698      * @cfg {Object} raw The raw response returned from server-code
36699      */
36700     raw: undefined,
36701     /**
36702      * @cfg {Ext.data.Record/Ext.data.Record[]} records related to the Request action
36703      */
36704     records: undefined
36705 };
36706 /**
36707  * @class Ext.data.ScriptTagProxy
36708  * @extends Ext.data.DataProxy
36709  * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain
36710  * other than the originating domain of the running page.<br>
36711  * <p>
36712  * <b>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
36713  * of the running page, you must use this class, rather than HttpProxy.</b><br>
36714  * <p>
36715  * The content passed back from a server resource requested by a ScriptTagProxy <b>must</b> be executable JavaScript
36716  * source code because it is used as the source inside a &lt;script> tag.<br>
36717  * <p>
36718  * In order for the browser to process the returned data, the server must wrap the data object
36719  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
36720  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
36721  * depending on whether the callback name was passed:
36722  * <p>
36723  * <pre><code>
36724 boolean scriptTag = false;
36725 String cb = request.getParameter("callback");
36726 if (cb != null) {
36727     scriptTag = true;
36728     response.setContentType("text/javascript");
36729 } else {
36730     response.setContentType("application/x-json");
36731 }
36732 Writer out = response.getWriter();
36733 if (scriptTag) {
36734     out.write(cb + "(");
36735 }
36736 out.print(dataBlock.toJsonString());
36737 if (scriptTag) {
36738     out.write(");");
36739 }
36740 </code></pre>
36741  * <p>Below is a PHP example to do the same thing:</p><pre><code>
36742 $callback = $_REQUEST['callback'];
36743
36744 // Create the output object.
36745 $output = array('a' => 'Apple', 'b' => 'Banana');
36746
36747 //start output
36748 if ($callback) {
36749     header('Content-Type: text/javascript');
36750     echo $callback . '(' . json_encode($output) . ');';
36751 } else {
36752     header('Content-Type: application/x-json');
36753     echo json_encode($output);
36754 }
36755 </code></pre>
36756  * <p>Below is the ASP.Net code to do the same thing:</p><pre><code>
36757 String jsonString = "{success: true}";
36758 String cb = Request.Params.Get("callback");
36759 String responseString = "";
36760 if (!String.IsNullOrEmpty(cb)) {
36761     responseString = cb + "(" + jsonString + ")";
36762 } else {
36763     responseString = jsonString;
36764 }
36765 Response.Write(responseString);
36766 </code></pre>
36767  *
36768  * @constructor
36769  * @param {Object} config A configuration object.
36770  */
36771 Ext.data.ScriptTagProxy = function(config){
36772     Ext.apply(this, config);
36773
36774     Ext.data.ScriptTagProxy.superclass.constructor.call(this, config);
36775
36776     this.head = document.getElementsByTagName("head")[0];
36777
36778     /**
36779      * @event loadexception
36780      * <b>Deprecated</b> in favor of 'exception' event.
36781      * Fires if an exception occurs in the Proxy during data loading.  This event can be fired for one of two reasons:
36782      * <ul><li><b>The load call timed out.</b>  This means the load callback did not execute within the time limit
36783      * specified by {@link #timeout}.  In this case, this event will be raised and the
36784      * fourth parameter (read error) will be null.</li>
36785      * <li><b>The load succeeded but the reader could not read the response.</b>  This means the server returned
36786      * data, but the configured Reader threw an error while reading the data.  In this case, this event will be
36787      * raised and the caught error will be passed along as the fourth parameter of this event.</li></ul>
36788      * Note that this event is also relayed through {@link Ext.data.Store}, so you can listen for it directly
36789      * on any Store instance.
36790      * @param {Object} this
36791      * @param {Object} options The loading options that were specified (see {@link #load} for details).  If the load
36792      * call timed out, this parameter will be null.
36793      * @param {Object} arg The callback's arg object passed to the {@link #load} function
36794      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data.
36795      * If the remote request returns success: false, this parameter will be null.
36796      */
36797 };
36798
36799 Ext.data.ScriptTagProxy.TRANS_ID = 1000;
36800
36801 Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {
36802     /**
36803      * @cfg {String} url The URL from which to request the data object.
36804      */
36805     /**
36806      * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
36807      */
36808     timeout : 30000,
36809     /**
36810      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
36811      * the server the name of the callback function set up by the load call to process the returned data object.
36812      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
36813      * javascript output which calls this named function passing the data object as its only parameter.
36814      */
36815     callbackParam : "callback",
36816     /**
36817      *  @cfg {Boolean} nocache (optional) Defaults to true. Disable caching by adding a unique parameter
36818      * name to the request.
36819      */
36820     nocache : true,
36821
36822     /**
36823      * HttpProxy implementation of DataProxy#doRequest
36824      * @param {String} action
36825      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is <tt>read</tt>, rs will be null
36826      * @param {Object} params An object containing properties which are to be used as HTTP parameters
36827      * for the request to the remote server.
36828      * @param {Ext.data.DataReader} reader The Reader object which converts the data
36829      * object into a block of Ext.data.Records.
36830      * @param {Function} callback The function into which to pass the block of Ext.data.Records.
36831      * The function must be passed <ul>
36832      * <li>The Record block object</li>
36833      * <li>The "arg" argument from the load function</li>
36834      * <li>A boolean success indicator</li>
36835      * </ul>
36836      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
36837      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
36838      */
36839     doRequest : function(action, rs, params, reader, callback, scope, arg) {
36840         var p = Ext.urlEncode(Ext.apply(params, this.extraParams));
36841
36842         var url = this.buildUrl(action, rs);
36843         if (!url) {
36844             throw new Ext.data.Api.Error('invalid-url', url);
36845         }
36846         url = Ext.urlAppend(url, p);
36847
36848         if(this.nocache){
36849             url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()));
36850         }
36851         var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;
36852         var trans = {
36853             id : transId,
36854             action: action,
36855             cb : "stcCallback"+transId,
36856             scriptId : "stcScript"+transId,
36857             params : params,
36858             arg : arg,
36859             url : url,
36860             callback : callback,
36861             scope : scope,
36862             reader : reader
36863         };
36864         window[trans.cb] = this.createCallback(action, rs, trans);
36865         url += String.format("&{0}={1}", this.callbackParam, trans.cb);
36866         if(this.autoAbort !== false){
36867             this.abort();
36868         }
36869
36870         trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
36871
36872         var script = document.createElement("script");
36873         script.setAttribute("src", url);
36874         script.setAttribute("type", "text/javascript");
36875         script.setAttribute("id", trans.scriptId);
36876         this.head.appendChild(script);
36877
36878         this.trans = trans;
36879     },
36880
36881     // @private createCallback
36882     createCallback : function(action, rs, trans) {
36883         var self = this;
36884         return function(res) {
36885             self.trans = false;
36886             self.destroyTrans(trans, true);
36887             if (action === Ext.data.Api.actions.read) {
36888                 self.onRead.call(self, action, trans, res);
36889             } else {
36890                 self.onWrite.call(self, action, trans, res, rs);
36891             }
36892         };
36893     },
36894     /**
36895      * Callback for read actions
36896      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36897      * @param {Object} trans The request transaction object
36898      * @param {Object} res The server response
36899      * @protected
36900      */
36901     onRead : function(action, trans, res) {
36902         var result;
36903         try {
36904             result = trans.reader.readRecords(res);
36905         }catch(e){
36906             // @deprecated: fire loadexception
36907             this.fireEvent("loadexception", this, trans, res, e);
36908
36909             this.fireEvent('exception', this, 'response', action, trans, res, e);
36910             trans.callback.call(trans.scope||window, null, trans.arg, false);
36911             return;
36912         }
36913         if (result.success === false) {
36914             // @deprecated: fire old loadexception for backwards-compat.
36915             this.fireEvent('loadexception', this, trans, res);
36916
36917             this.fireEvent('exception', this, 'remote', action, trans, res, null);
36918         } else {
36919             this.fireEvent("load", this, res, trans.arg);
36920         }
36921         trans.callback.call(trans.scope||window, result, trans.arg, result.success);
36922     },
36923     /**
36924      * Callback for write actions
36925      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36926      * @param {Object} trans The request transaction object
36927      * @param {Object} res The server response
36928      * @protected
36929      */
36930     onWrite : function(action, trans, response, rs) {
36931         var reader = trans.reader;
36932         try {
36933             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.
36934             var res = reader.readResponse(action, response);
36935         } catch (e) {
36936             this.fireEvent('exception', this, 'response', action, trans, res, e);
36937             trans.callback.call(trans.scope||window, null, res, false);
36938             return;
36939         }
36940         if(!res.success === true){
36941             this.fireEvent('exception', this, 'remote', action, trans, res, rs);
36942             trans.callback.call(trans.scope||window, null, res, false);
36943             return;
36944         }
36945         this.fireEvent("write", this, action, res.data, res, rs, trans.arg );
36946         trans.callback.call(trans.scope||window, res.data, res, true);
36947     },
36948
36949     // private
36950     isLoading : function(){
36951         return this.trans ? true : false;
36952     },
36953
36954     /**
36955      * Abort the current server request.
36956      */
36957     abort : function(){
36958         if(this.isLoading()){
36959             this.destroyTrans(this.trans);
36960         }
36961     },
36962
36963     // private
36964     destroyTrans : function(trans, isLoaded){
36965         this.head.removeChild(document.getElementById(trans.scriptId));
36966         clearTimeout(trans.timeoutId);
36967         if(isLoaded){
36968             window[trans.cb] = undefined;
36969             try{
36970                 delete window[trans.cb];
36971             }catch(e){}
36972         }else{
36973             // if hasn't been loaded, wait for load to remove it to prevent script error
36974             window[trans.cb] = function(){
36975                 window[trans.cb] = undefined;
36976                 try{
36977                     delete window[trans.cb];
36978                 }catch(e){}
36979             };
36980         }
36981     },
36982
36983     // private
36984     handleFailure : function(trans){
36985         this.trans = false;
36986         this.destroyTrans(trans, false);
36987         if (trans.action === Ext.data.Api.actions.read) {
36988             // @deprecated firing loadexception
36989             this.fireEvent("loadexception", this, null, trans.arg);
36990         }
36991
36992         this.fireEvent('exception', this, 'response', trans.action, {
36993             response: null,
36994             options: trans.arg
36995         });
36996         trans.callback.call(trans.scope||window, null, trans.arg, false);
36997     },
36998
36999     // inherit docs
37000     destroy: function(){
37001         this.abort();
37002         Ext.data.ScriptTagProxy.superclass.destroy.call(this);
37003     }
37004 });/**
37005  * @class Ext.data.HttpProxy
37006  * @extends Ext.data.DataProxy
37007  * <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same
37008  * domain of the originating page.</p>
37009  * <p><b>Note</b>: this class cannot be used to retrieve data from a domain other
37010  * than the domain from which the running page was served. For cross-domain requests, use a
37011  * {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>
37012  * <p>Be aware that to enable the browser to parse an XML document, the server must set
37013  * the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>
37014  * @constructor
37015  * @param {Object} conn
37016  * An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}.
37017  * <p>Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the
37018  * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>
37019  * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,
37020  * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be
37021  * used to pass parameters known at instantiation time.</p>
37022  * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make
37023  * the request.</p>
37024  */
37025 Ext.data.HttpProxy = function(conn){
37026     Ext.data.HttpProxy.superclass.constructor.call(this, conn);
37027
37028     /**
37029      * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy
37030      * uses to make requests to the server. Properties of this object may be changed dynamically to
37031      * change the way data is requested.
37032      * @property
37033      */
37034     this.conn = conn;
37035
37036     // nullify the connection url.  The url param has been copied to 'this' above.  The connection
37037     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the
37038     // connection url during beforeaction events (ie: beforeload, beforewrite, etc).
37039     // Url is always re-defined during doRequest.
37040     this.conn.url = null;
37041
37042     this.useAjax = !conn || !conn.events;
37043
37044     // A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]
37045     var actions = Ext.data.Api.actions;
37046     this.activeRequest = {};
37047     for (var verb in actions) {
37048         this.activeRequest[actions[verb]] = undefined;
37049     }
37050 };
37051
37052 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {
37053     /**
37054      * Return the {@link Ext.data.Connection} object being used by this Proxy.
37055      * @return {Connection} The Connection object. This object may be used to subscribe to events on
37056      * a finer-grained basis than the DataProxy events.
37057      */
37058     getConnection : function() {
37059         return this.useAjax ? Ext.Ajax : this.conn;
37060     },
37061
37062     /**
37063      * Used for overriding the url used for a single request.  Designed to be called during a beforeaction event.  Calling setUrl
37064      * will override any urls set via the api configuration parameter.  Set the optional parameter makePermanent to set the url for
37065      * all subsequent requests.  If not set to makePermanent, the next request will use the same url or api configuration defined
37066      * in the initial proxy configuration.
37067      * @param {String} url
37068      * @param {Boolean} makePermanent (Optional) [false]
37069      *
37070      * (e.g.: beforeload, beforesave, etc).
37071      */
37072     setUrl : function(url, makePermanent) {
37073         this.conn.url = url;
37074         if (makePermanent === true) {
37075             this.url = url;
37076             this.api = null;
37077             Ext.data.Api.prepare(this);
37078         }
37079     },
37080
37081     /**
37082      * HttpProxy implementation of DataProxy#doRequest
37083      * @param {String} action The crud action type (create, read, update, destroy)
37084      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
37085      * @param {Object} params An object containing properties which are to be used as HTTP parameters
37086      * for the request to the remote server.
37087      * @param {Ext.data.DataReader} reader The Reader object which converts the data
37088      * object into a block of Ext.data.Records.
37089      * @param {Function} callback
37090      * <div class="sub-desc"><p>A function to be called after the request.
37091      * The <tt>callback</tt> is passed the following arguments:<ul>
37092      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
37093      * <li><tt>options</tt>: Options object from the action request</li>
37094      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
37095      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
37096      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
37097      * @protected
37098      */
37099     doRequest : function(action, rs, params, reader, cb, scope, arg) {
37100         var  o = {
37101             method: (this.api[action]) ? this.api[action]['method'] : undefined,
37102             request: {
37103                 callback : cb,
37104                 scope : scope,
37105                 arg : arg
37106             },
37107             reader: reader,
37108             callback : this.createCallback(action, rs),
37109             scope: this
37110         };
37111
37112         // If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).
37113         // Use std HTTP params otherwise.
37114         if (params.jsonData) {
37115             o.jsonData = params.jsonData;
37116         } else if (params.xmlData) {
37117             o.xmlData = params.xmlData;
37118         } else {
37119             o.params = params || {};
37120         }
37121         // Set the connection url.  If this.conn.url is not null here,
37122         // the user must have overridden the url during a beforewrite/beforeload event-handler.
37123         // this.conn.url is nullified after each request.
37124         this.conn.url = this.buildUrl(action, rs);
37125
37126         if(this.useAjax){
37127
37128             Ext.applyIf(o, this.conn);
37129
37130             // If a currently running request is found for this action, abort it.
37131             if (this.activeRequest[action]) {
37132                 ////
37133                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array
37134                 // TODO ideas anyone?
37135                 //
37136                 //Ext.Ajax.abort(this.activeRequest[action]);
37137             }
37138             this.activeRequest[action] = Ext.Ajax.request(o);
37139         }else{
37140             this.conn.request(o);
37141         }
37142         // request is sent, nullify the connection url in preparation for the next request
37143         this.conn.url = null;
37144     },
37145
37146     /**
37147      * Returns a callback function for a request.  Note a special case is made for the
37148      * read action vs all the others.
37149      * @param {String} action [create|update|delete|load]
37150      * @param {Ext.data.Record[]} rs The Store-recordset being acted upon
37151      * @private
37152      */
37153     createCallback : function(action, rs) {
37154         return function(o, success, response) {
37155             this.activeRequest[action] = undefined;
37156             if (!success) {
37157                 if (action === Ext.data.Api.actions.read) {
37158                     // @deprecated: fire loadexception for backwards compat.
37159                     // TODO remove
37160                     this.fireEvent('loadexception', this, o, response);
37161                 }
37162                 this.fireEvent('exception', this, 'response', action, o, response);
37163                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37164                 return;
37165             }
37166             if (action === Ext.data.Api.actions.read) {
37167                 this.onRead(action, o, response);
37168             } else {
37169                 this.onWrite(action, o, response, rs);
37170             }
37171         };
37172     },
37173
37174     /**
37175      * Callback for read action
37176      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
37177      * @param {Object} o The request transaction object
37178      * @param {Object} res The server response
37179      * @fires loadexception (deprecated)
37180      * @fires exception
37181      * @fires load
37182      * @protected
37183      */
37184     onRead : function(action, o, response) {
37185         var result;
37186         try {
37187             result = o.reader.read(response);
37188         }catch(e){
37189             // @deprecated: fire old loadexception for backwards-compat.
37190             // TODO remove
37191             this.fireEvent('loadexception', this, o, response, e);
37192
37193             this.fireEvent('exception', this, 'response', action, o, response, e);
37194             o.request.callback.call(o.request.scope, null, o.request.arg, false);
37195             return;
37196         }
37197         if (result.success === false) {
37198             // @deprecated: fire old loadexception for backwards-compat.
37199             // TODO remove
37200             this.fireEvent('loadexception', this, o, response);
37201
37202             // Get DataReader read-back a response-object to pass along to exception event
37203             var res = o.reader.readResponse(action, response);
37204             this.fireEvent('exception', this, 'remote', action, o, res, null);
37205         }
37206         else {
37207             this.fireEvent('load', this, o, o.request.arg);
37208         }
37209         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
37210         // the calls to request.callback(...) in each will have to be made identical.
37211         // NOTE reader.readResponse does not currently return Ext.data.Response
37212         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);
37213     },
37214     /**
37215      * Callback for write actions
37216      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
37217      * @param {Object} trans The request transaction object
37218      * @param {Object} res The server response
37219      * @fires exception
37220      * @fires write
37221      * @protected
37222      */
37223     onWrite : function(action, o, response, rs) {
37224         var reader = o.reader;
37225         var res;
37226         try {
37227             res = reader.readResponse(action, response);
37228         } catch (e) {
37229             this.fireEvent('exception', this, 'response', action, o, response, e);
37230             o.request.callback.call(o.request.scope, null, o.request.arg, false);
37231             return;
37232         }
37233         if (res.success === true) {
37234             this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);
37235         } else {
37236             this.fireEvent('exception', this, 'remote', action, o, res, rs);
37237         }
37238         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
37239         // the calls to request.callback(...) in each will have to be made similar.
37240         // NOTE reader.readResponse does not currently return Ext.data.Response
37241         o.request.callback.call(o.request.scope, res.data, res, res.success);
37242     },
37243
37244     // inherit docs
37245     destroy: function(){
37246         if(!this.useAjax){
37247             this.conn.abort();
37248         }else if(this.activeRequest){
37249             var actions = Ext.data.Api.actions;
37250             for (var verb in actions) {
37251                 if(this.activeRequest[actions[verb]]){
37252                     Ext.Ajax.abort(this.activeRequest[actions[verb]]);
37253                 }
37254             }
37255         }
37256         Ext.data.HttpProxy.superclass.destroy.call(this);
37257     }
37258 });/**
37259  * @class Ext.data.MemoryProxy
37260  * @extends Ext.data.DataProxy
37261  * An implementation of Ext.data.DataProxy that simply passes the data specified in its constructor
37262  * to the Reader when its load method is called.
37263  * @constructor
37264  * @param {Object} data The data object which the Reader uses to construct a block of Ext.data.Records.
37265  */
37266 Ext.data.MemoryProxy = function(data){
37267     // Must define a dummy api with "read" action to satisfy DataProxy#doRequest and Ext.data.Api#prepare *before* calling super
37268     var api = {};
37269     api[Ext.data.Api.actions.read] = true;
37270     Ext.data.MemoryProxy.superclass.constructor.call(this, {
37271         api: api
37272     });
37273     this.data = data;
37274 };
37275
37276 Ext.extend(Ext.data.MemoryProxy, Ext.data.DataProxy, {
37277     /**
37278      * @event loadexception
37279      * Fires if an exception occurs in the Proxy during data loading. Note that this event is also relayed
37280      * through {@link Ext.data.Store}, so you can listen for it directly on any Store instance.
37281      * @param {Object} this
37282      * @param {Object} arg The callback's arg object passed to the {@link #load} function
37283      * @param {Object} null This parameter does not apply and will always be null for MemoryProxy
37284      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data
37285      */
37286
37287        /**
37288      * MemoryProxy implementation of DataProxy#doRequest
37289      * @param {String} action
37290      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
37291      * @param {Object} params An object containing properties which are to be used as HTTP parameters
37292      * for the request to the remote server.
37293      * @param {Ext.data.DataReader} reader The Reader object which converts the data
37294      * object into a block of Ext.data.Records.
37295      * @param {Function} callback The function into which to pass the block of Ext.data.Records.
37296      * The function must be passed <ul>
37297      * <li>The Record block object</li>
37298      * <li>The "arg" argument from the load function</li>
37299      * <li>A boolean success indicator</li>
37300      * </ul>
37301      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
37302      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
37303      */
37304     doRequest : function(action, rs, params, reader, callback, scope, arg) {
37305         // No implementation for CRUD in MemoryProxy.  Assumes all actions are 'load'
37306         params = params || {};
37307         var result;
37308         try {
37309             result = reader.readRecords(this.data);
37310         }catch(e){
37311             // @deprecated loadexception
37312             this.fireEvent("loadexception", this, null, arg, e);
37313
37314             this.fireEvent('exception', this, 'response', action, arg, null, e);
37315             callback.call(scope, null, arg, false);
37316             return;
37317         }
37318         callback.call(scope, result, arg, true);
37319     }
37320 });/**
37321  * @class Ext.data.Types
37322  * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
37323  * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
37324  * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
37325  * of this class.</p>
37326  * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
37327  * each type definition must contain three properties:</p>
37328  * <div class="mdetail-params"><ul>
37329  * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
37330  * to be stored in the Field. The function is passed the collowing parameters:
37331  * <div class="mdetail-params"><ul>
37332  * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
37333  * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
37334  * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
37335  * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
37336  * ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
37337  * </ul></div></div></li>
37338  * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
37339  * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
37340  * </ul></div>
37341  * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
37342  * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
37343  *<pre><code>
37344 // Add a new Field data type which stores a VELatLong object in the Record.
37345 Ext.data.Types.VELATLONG = {
37346     convert: function(v, data) {
37347         return new VELatLong(data.lat, data.long);
37348     },
37349     sortType: function(v) {
37350         return v.Latitude;  // When sorting, order by latitude
37351     },
37352     type: 'VELatLong'
37353 };
37354 </code></pre>
37355  * <p>Then, when declaring a Record, use <pre><code>
37356 var types = Ext.data.Types; // allow shorthand type access
37357 UnitRecord = Ext.data.Record.create([
37358     { name: 'unitName', mapping: 'UnitName' },
37359     { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
37360     { name: 'latitude', mapping: 'lat', type: types.FLOAT },
37361     { name: 'latitude', mapping: 'lat', type: types.FLOAT },
37362     { name: 'position', type: types.VELATLONG }
37363 ]);
37364 </code></pre>
37365  * @singleton
37366  */
37367 Ext.data.Types = new function(){
37368     var st = Ext.data.SortTypes;
37369     Ext.apply(this, {
37370         /**
37371          * @type Regexp
37372          * @property stripRe
37373          * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
37374          * This should be overridden for localization.
37375          */
37376         stripRe: /[\$,%]/g,
37377         
37378         /**
37379          * @type Object.
37380          * @property AUTO
37381          * This data type means that no conversion is applied to the raw data before it is placed into a Record.
37382          */
37383         AUTO: {
37384             convert: function(v){ return v; },
37385             sortType: st.none,
37386             type: 'auto'
37387         },
37388
37389         /**
37390          * @type Object.
37391          * @property STRING
37392          * This data type means that the raw data is converted into a String before it is placed into a Record.
37393          */
37394         STRING: {
37395             convert: function(v){ return (v === undefined || v === null) ? '' : String(v); },
37396             sortType: st.asUCString,
37397             type: 'string'
37398         },
37399
37400         /**
37401          * @type Object.
37402          * @property INT
37403          * This data type means that the raw data is converted into an integer before it is placed into a Record.
37404          * <p>The synonym <code>INTEGER</code> is equivalent.</p>
37405          */
37406         INT: {
37407             convert: function(v){
37408                 return v !== undefined && v !== null && v !== '' ?
37409                     parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : 0;
37410             },
37411             sortType: st.none,
37412             type: 'int'
37413         },
37414         
37415         /**
37416          * @type Object.
37417          * @property FLOAT
37418          * This data type means that the raw data is converted into a number before it is placed into a Record.
37419          * <p>The synonym <code>NUMBER</code> is equivalent.</p>
37420          */
37421         FLOAT: {
37422             convert: function(v){
37423                 return v !== undefined && v !== null && v !== '' ?
37424                     parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : 0;
37425             },
37426             sortType: st.none,
37427             type: 'float'
37428         },
37429         
37430         /**
37431          * @type Object.
37432          * @property BOOL
37433          * <p>This data type means that the raw data is converted into a boolean before it is placed into
37434          * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
37435          * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
37436          */
37437         BOOL: {
37438             convert: function(v){ return v === true || v === 'true' || v == 1; },
37439             sortType: st.none,
37440             type: 'bool'
37441         },
37442         
37443         /**
37444          * @type Object.
37445          * @property DATE
37446          * This data type means that the raw data is converted into a Date before it is placed into a Record.
37447          * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
37448          * being applied.
37449          */
37450         DATE: {
37451             convert: function(v){
37452                 var df = this.dateFormat;
37453                 if(!v){
37454                     return null;
37455                 }
37456                 if(Ext.isDate(v)){
37457                     return v;
37458                 }
37459                 if(df){
37460                     if(df == 'timestamp'){
37461                         return new Date(v*1000);
37462                     }
37463                     if(df == 'time'){
37464                         return new Date(parseInt(v, 10));
37465                     }
37466                     return Date.parseDate(v, df);
37467                 }
37468                 var parsed = Date.parse(v);
37469                 return parsed ? new Date(parsed) : null;
37470             },
37471             sortType: st.asDate,
37472             type: 'date'
37473         }
37474     });
37475     
37476     Ext.apply(this, {
37477         /**
37478          * @type Object.
37479          * @property BOOLEAN
37480          * <p>This data type means that the raw data is converted into a boolean before it is placed into
37481          * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
37482          * <p>The synonym <code>BOOL</code> is equivalent.</p>
37483          */
37484         BOOLEAN: this.BOOL,
37485         /**
37486          * @type Object.
37487          * @property INTEGER
37488          * This data type means that the raw data is converted into an integer before it is placed into a Record.
37489          * <p>The synonym <code>INT</code> is equivalent.</p>
37490          */
37491         INTEGER: this.INT,
37492         /**
37493          * @type Object.
37494          * @property NUMBER
37495          * This data type means that the raw data is converted into a number before it is placed into a Record.
37496          * <p>The synonym <code>FLOAT</code> is equivalent.</p>
37497          */
37498         NUMBER: this.FLOAT    
37499     });
37500 };/**
37501  * @class Ext.data.JsonWriter
37502  * @extends Ext.data.DataWriter
37503  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action.
37504  */
37505 Ext.data.JsonWriter = Ext.extend(Ext.data.DataWriter, {
37506     /**
37507      * @cfg {Boolean} encode <tt>true</tt> to {@link Ext.util.JSON#encode encode} the
37508      * {@link Ext.data.DataWriter#toHash hashed data}. Defaults to <tt>true</tt>.  When using
37509      * {@link Ext.data.DirectProxy}, set this to <tt>false</tt> since Ext.Direct.JsonProvider will perform
37510      * its own json-encoding.  In addition, if you're using {@link Ext.data.HttpProxy}, setting to <tt>false</tt>
37511      * will cause HttpProxy to transmit data using the <b>jsonData</b> configuration-params of {@link Ext.Ajax#request}
37512      * instead of <b>params</b>.  When using a {@link Ext.data.Store#restful} Store, some serverside frameworks are
37513      * tuned to expect data through the jsonData mechanism.  In those cases, one will want to set <b>encode: <tt>false</tt></b>, as in
37514      * let the lower-level connection object (eg: Ext.Ajax) do the encoding.
37515      */
37516     encode : true,
37517     /**
37518      * @cfg {Boolean} encodeDelete False to send only the id to the server on delete, true to encode it in an object
37519      * literal, eg: <pre><code>
37520 {id: 1}
37521  * </code></pre> Defaults to <tt>false</tt>
37522      */
37523     encodeDelete: false,
37524     
37525     constructor : function(config){
37526         Ext.data.JsonWriter.superclass.constructor.call(this, config);    
37527     },
37528
37529     /**
37530      * Final action of a write event.  Apply the written data-object to params.
37531      * @param {Object} http params-object to write-to.
37532      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
37533      * @param {Object/Object[]} data Data-object representing compiled Store-recordset.
37534      */
37535     render : function(params, baseParams, data) {
37536         if (this.encode === true) {
37537             // Encode here now.
37538             Ext.apply(params, baseParams);
37539             params[this.meta.root] = Ext.encode(data);
37540         } else {
37541             // defer encoding for some other layer, probably in {@link Ext.Ajax#request}.  Place everything into "jsonData" key.
37542             var jdata = Ext.apply({}, baseParams);
37543             jdata[this.meta.root] = data;
37544             params.jsonData = jdata;
37545         }
37546     },
37547     /**
37548      * Implements abstract Ext.data.DataWriter#createRecord
37549      * @protected
37550      * @param {Ext.data.Record} rec
37551      * @return {Object}
37552      */
37553     createRecord : function(rec) {
37554        return this.toHash(rec);
37555     },
37556     /**
37557      * Implements abstract Ext.data.DataWriter#updateRecord
37558      * @protected
37559      * @param {Ext.data.Record} rec
37560      * @return {Object}
37561      */
37562     updateRecord : function(rec) {
37563         return this.toHash(rec);
37564
37565     },
37566     /**
37567      * Implements abstract Ext.data.DataWriter#destroyRecord
37568      * @protected
37569      * @param {Ext.data.Record} rec
37570      * @return {Object}
37571      */
37572     destroyRecord : function(rec){
37573         if(this.encodeDelete){
37574             var data = {};
37575             data[this.meta.idProperty] = rec.id;
37576             return data;
37577         }else{
37578             return rec.id;
37579         }
37580     }
37581 });/**
37582  * @class Ext.data.JsonReader
37583  * @extends Ext.data.DataReader
37584  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects
37585  * from a JSON packet based on mappings in a provided {@link Ext.data.Record}
37586  * constructor.</p>
37587  * <p>Example code:</p>
37588  * <pre><code>
37589 var myReader = new Ext.data.JsonReader({
37590     // metadata configuration options:
37591     {@link #idProperty}: 'id'
37592     {@link #root}: 'rows',
37593     {@link #totalProperty}: 'results',
37594     {@link Ext.data.DataReader#messageProperty}: "msg"  // The element within the response that provides a user-feedback message (optional)
37595
37596     // the fields config option will internally create an {@link Ext.data.Record}
37597     // constructor that provides mapping for reading the record data objects
37598     {@link Ext.data.DataReader#fields fields}: [
37599         // map Record&#39;s 'firstname' field to data object&#39;s key of same name
37600         {name: 'name'},
37601         // map Record&#39;s 'job' field to data object&#39;s 'occupation' key
37602         {name: 'job', mapping: 'occupation'}
37603     ]
37604 });
37605 </code></pre>
37606  * <p>This would consume a JSON data object of the form:</p><pre><code>
37607 {
37608     results: 2000, // Reader&#39;s configured {@link #totalProperty}
37609     rows: [        // Reader&#39;s configured {@link #root}
37610         // record data objects:
37611         { {@link #idProperty id}: 1, firstname: 'Bill', occupation: 'Gardener' },
37612         { {@link #idProperty id}: 2, firstname: 'Ben' , occupation: 'Horticulturalist' },
37613         ...
37614     ]
37615 }
37616 </code></pre>
37617  * <p><b><u>Automatic configuration using metaData</u></b></p>
37618  * <p>It is possible to change a JsonReader's metadata at any time by including
37619  * a <b><tt>metaData</tt></b> property in the JSON data object. If the JSON data
37620  * object has a <b><tt>metaData</tt></b> property, a {@link Ext.data.Store Store}
37621  * object using this Reader will reconfigure itself to use the newly provided
37622  * field definition and fire its {@link Ext.data.Store#metachange metachange}
37623  * event. The metachange event handler may interrogate the <b><tt>metaData</tt></b>
37624  * property to perform any configuration required.</p>
37625  * <p>Note that reconfiguring a Store potentially invalidates objects which may
37626  * refer to Fields or Records which no longer exist.</p>
37627  * <p>To use this facility you would create the JsonReader like this:</p><pre><code>
37628 var myReader = new Ext.data.JsonReader();
37629 </code></pre>
37630  * <p>The first data packet from the server would configure the reader by
37631  * containing a <b><tt>metaData</tt></b> property <b>and</b> the data. For
37632  * example, the JSON data object might take the form:</p><pre><code>
37633 {
37634     metaData: {
37635         "{@link #idProperty}": "id",
37636         "{@link #root}": "rows",
37637         "{@link #totalProperty}": "results"
37638         "{@link #successProperty}": "success",
37639         "{@link Ext.data.DataReader#fields fields}": [
37640             {"name": "name"},
37641             {"name": "job", "mapping": "occupation"}
37642         ],
37643         // used by store to set its sortInfo
37644         "sortInfo":{
37645            "field": "name",
37646            "direction": "ASC"
37647         },
37648         // {@link Ext.PagingToolbar paging data} (if applicable)
37649         "start": 0,
37650         "limit": 2,
37651         // custom property
37652         "foo": "bar"
37653     },
37654     // Reader&#39;s configured {@link #successProperty}
37655     "success": true,
37656     // Reader&#39;s configured {@link #totalProperty}
37657     "results": 2000,
37658     // Reader&#39;s configured {@link #root}
37659     // (this data simulates 2 results {@link Ext.PagingToolbar per page})
37660     "rows": [ // <b>*Note:</b> this must be an Array
37661         { "id": 1, "name": "Bill", "occupation": "Gardener" },
37662         { "id": 2, "name":  "Ben", "occupation": "Horticulturalist" }
37663     ]
37664 }
37665  * </code></pre>
37666  * <p>The <b><tt>metaData</tt></b> property in the JSON data object should contain:</p>
37667  * <div class="mdetail-params"><ul>
37668  * <li>any of the configuration options for this class</li>
37669  * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which
37670  * the JsonReader will use as an argument to the
37671  * {@link Ext.data.Record#create data Record create method} in order to
37672  * configure the layout of the Records it will produce.</li>
37673  * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property
37674  * which the JsonReader will use to set the {@link Ext.data.Store}'s
37675  * {@link Ext.data.Store#sortInfo sortInfo} property</li>
37676  * <li>any custom properties needed</li>
37677  * </ul></div>
37678  *
37679  * @constructor
37680  * Create a new JsonReader
37681  * @param {Object} meta Metadata configuration options.
37682  * @param {Array/Object} recordType
37683  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
37684  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
37685  * constructor created from {@link Ext.data.Record#create}.</p>
37686  */
37687 Ext.data.JsonReader = function(meta, recordType){
37688     meta = meta || {};
37689     /**
37690      * @cfg {String} idProperty [id] Name of the property within a row object
37691      * that contains a record identifier value.  Defaults to <tt>id</tt>
37692      */
37693     /**
37694      * @cfg {String} successProperty [success] Name of the property from which to
37695      * retrieve the success attribute. Defaults to <tt>success</tt>.  See
37696      * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
37697      * for additional information.
37698      */
37699     /**
37700      * @cfg {String} totalProperty [total] Name of the property from which to
37701      * retrieve the total number of records in the dataset. This is only needed
37702      * if the whole dataset is not passed in one go, but is being paged from
37703      * the remote server.  Defaults to <tt>total</tt>.
37704      */
37705     /**
37706      * @cfg {String} root [undefined] <b>Required</b>.  The name of the property
37707      * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
37708      * An exception will be thrown if the root property is undefined. The data
37709      * packet value for this property should be an empty array to clear the data
37710      * or show no data.
37711      */
37712     Ext.applyIf(meta, {
37713         idProperty: 'id',
37714         successProperty: 'success',
37715         totalProperty: 'total'
37716     });
37717
37718     Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
37719 };
37720 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
37721     /**
37722      * This JsonReader's metadata as passed to the constructor, or as passed in
37723      * the last data packet's <b><tt>metaData</tt></b> property.
37724      * @type Mixed
37725      * @property meta
37726      */
37727     /**
37728      * This method is only used by a DataProxy which has retrieved data from a remote server.
37729      * @param {Object} response The XHR object which contains the JSON data in its responseText.
37730      * @return {Object} data A data block which is used by an Ext.data.Store object as
37731      * a cache of Ext.data.Records.
37732      */
37733     read : function(response){
37734         var json = response.responseText;
37735         var o = Ext.decode(json);
37736         if(!o) {
37737             throw {message: 'JsonReader.read: Json object not found'};
37738         }
37739         return this.readRecords(o);
37740     },
37741
37742     /*
37743      * TODO: refactor code between JsonReader#readRecords, #readResponse into 1 method.
37744      * there's ugly duplication going on due to maintaining backwards compat. with 2.0.  It's time to do this.
37745      */
37746     /**
37747      * Decode a JSON response from server.
37748      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
37749      * @param {Object} response The XHR object returned through an Ajax server request.
37750      * @return {Response} A {@link Ext.data.Response Response} object containing the data response, and also status information.
37751      */
37752     readResponse : function(action, response) {
37753         var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
37754         if(!o) {
37755             throw new Ext.data.JsonReader.Error('response');
37756         }
37757
37758         var root = this.getRoot(o);
37759         if (action === Ext.data.Api.actions.create) {
37760             var def = Ext.isDefined(root);
37761             if (def && Ext.isEmpty(root)) {
37762                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
37763             }
37764             else if (!def) {
37765                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
37766             }
37767         }
37768
37769         // instantiate response object
37770         var res = new Ext.data.Response({
37771             action: action,
37772             success: this.getSuccess(o),
37773             data: (root) ? this.extractData(root, false) : [],
37774             message: this.getMessage(o),
37775             raw: o
37776         });
37777
37778         // blow up if no successProperty
37779         if (Ext.isEmpty(res.success)) {
37780             throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
37781         }
37782         return res;
37783     },
37784
37785     /**
37786      * Create a data block containing Ext.data.Records from a JSON object.
37787      * @param {Object} o An object which contains an Array of row objects in the property specified
37788      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
37789      * which contains the total size of the dataset.
37790      * @return {Object} data A data block which is used by an Ext.data.Store object as
37791      * a cache of Ext.data.Records.
37792      */
37793     readRecords : function(o){
37794         /**
37795          * After any data loads, the raw JSON data is available for further custom processing.  If no data is
37796          * loaded or there is a load exception this property will be undefined.
37797          * @type Object
37798          */
37799         this.jsonData = o;
37800         if(o.metaData){
37801             this.onMetaChange(o.metaData);
37802         }
37803         var s = this.meta, Record = this.recordType,
37804             f = Record.prototype.fields, fi = f.items, fl = f.length, v;
37805
37806         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
37807         if(s.totalProperty){
37808             v = parseInt(this.getTotal(o), 10);
37809             if(!isNaN(v)){
37810                 totalRecords = v;
37811             }
37812         }
37813         if(s.successProperty){
37814             v = this.getSuccess(o);
37815             if(v === false || v === 'false'){
37816                 success = false;
37817             }
37818         }
37819
37820         // TODO return Ext.data.Response instance instead.  @see #readResponse
37821         return {
37822             success : success,
37823             records : this.extractData(root, true), // <-- true to return [Ext.data.Record]
37824             totalRecords : totalRecords
37825         };
37826     },
37827
37828     // private
37829     buildExtractors : function() {
37830         if(this.ef){
37831             return;
37832         }
37833         var s = this.meta, Record = this.recordType,
37834             f = Record.prototype.fields, fi = f.items, fl = f.length;
37835
37836         if(s.totalProperty) {
37837             this.getTotal = this.createAccessor(s.totalProperty);
37838         }
37839         if(s.successProperty) {
37840             this.getSuccess = this.createAccessor(s.successProperty);
37841         }
37842         if (s.messageProperty) {
37843             this.getMessage = this.createAccessor(s.messageProperty);
37844         }
37845         this.getRoot = s.root ? this.createAccessor(s.root) : function(p){return p;};
37846         if (s.id || s.idProperty) {
37847             var g = this.createAccessor(s.id || s.idProperty);
37848             this.getId = function(rec) {
37849                 var r = g(rec);
37850                 return (r === undefined || r === '') ? null : r;
37851             };
37852         } else {
37853             this.getId = function(){return null;};
37854         }
37855         var ef = [];
37856         for(var i = 0; i < fl; i++){
37857             f = fi[i];
37858             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
37859             ef.push(this.createAccessor(map));
37860         }
37861         this.ef = ef;
37862     },
37863
37864     /**
37865      * @ignore
37866      * TODO This isn't used anywhere??  Don't we want to use this where possible instead of complex #createAccessor?
37867      */
37868     simpleAccess : function(obj, subsc) {
37869         return obj[subsc];
37870     },
37871
37872     /**
37873      * @ignore
37874      */
37875     createAccessor : function(){
37876         var re = /[\[\.]/;
37877         return function(expr) {
37878             if(Ext.isEmpty(expr)){
37879                 return Ext.emptyFn;
37880             }
37881             if(Ext.isFunction(expr)){
37882                 return expr;
37883             }
37884             var i = String(expr).search(re);
37885             if(i >= 0){
37886                 return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
37887             }
37888             return function(obj){
37889                 return obj[expr];
37890             };
37891
37892         };
37893     }(),
37894
37895     /**
37896      * type-casts a single row of raw-data from server
37897      * @param {Object} data
37898      * @param {Array} items
37899      * @param {Integer} len
37900      * @private
37901      */
37902     extractValues : function(data, items, len) {
37903         var f, values = {};
37904         for(var j = 0; j < len; j++){
37905             f = items[j];
37906             var v = this.ef[j](data);
37907             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
37908         }
37909         return values;
37910     }
37911 });
37912
37913 /**
37914  * @class Ext.data.JsonReader.Error
37915  * Error class for JsonReader
37916  */
37917 Ext.data.JsonReader.Error = Ext.extend(Ext.Error, {
37918     constructor : function(message, arg) {
37919         this.arg = arg;
37920         Ext.Error.call(this, message);
37921     },
37922     name : 'Ext.data.JsonReader'
37923 });
37924 Ext.apply(Ext.data.JsonReader.Error.prototype, {
37925     lang: {
37926         'response': 'An error occurred while json-decoding your server response',
37927         'successProperty-response': 'Could not locate your "successProperty" in your server response.  Please review your JsonReader config to ensure the config-property "successProperty" matches the property in your server-response.  See the JsonReader docs.',
37928         'root-undefined-config': 'Your JsonReader was configured without a "root" property.  Please review your JsonReader config and make sure to define the root property.  See the JsonReader docs.',
37929         'idProperty-undefined' : 'Your JsonReader was configured without an "idProperty"  Please review your JsonReader configuration and ensure the "idProperty" is set (e.g.: "id").  See the JsonReader docs.',
37930         'root-empty': 'Data was expected to be returned by the server in the "root" property of the response.  Please review your JsonReader configuration to ensure the "root" property matches that returned in the server-response.  See JsonReader docs.'
37931     }
37932 });
37933 /**
37934  * @class Ext.data.ArrayReader
37935  * @extends Ext.data.JsonReader
37936  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an Array.
37937  * Each element of that Array represents a row of data fields. The
37938  * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
37939  * of the field definition if it exists, or the field's ordinal position in the definition.</p>
37940  * <p>Example code:</p>
37941  * <pre><code>
37942 var Employee = Ext.data.Record.create([
37943     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
37944     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
37945 ]);
37946 var myReader = new Ext.data.ArrayReader({
37947     {@link #idIndex}: 0
37948 }, Employee);
37949 </code></pre>
37950  * <p>This would consume an Array like this:</p>
37951  * <pre><code>
37952 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
37953  * </code></pre>
37954  * @constructor
37955  * Create a new ArrayReader
37956  * @param {Object} meta Metadata configuration options.
37957  * @param {Array/Object} recordType
37958  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
37959  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
37960  * constructor created from {@link Ext.data.Record#create}.</p>
37961  */
37962 Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
37963     /**
37964      * @cfg {String} successProperty
37965      * @hide
37966      */
37967     /**
37968      * @cfg {Number} id (optional) The subscript within row Array that provides an ID for the Record.
37969      * Deprecated. Use {@link #idIndex} instead.
37970      */
37971     /**
37972      * @cfg {Number} idIndex (optional) The subscript within row Array that provides an ID for the Record.
37973      */
37974     /**
37975      * Create a data block containing Ext.data.Records from an Array.
37976      * @param {Object} o An Array of row objects which represents the dataset.
37977      * @return {Object} data A data block which is used by an Ext.data.Store object as
37978      * a cache of Ext.data.Records.
37979      */
37980     readRecords : function(o){
37981         this.arrayData = o;
37982         var s = this.meta,
37983             sid = s ? Ext.num(s.idIndex, s.id) : null,
37984             recordType = this.recordType,
37985             fields = recordType.prototype.fields,
37986             records = [],
37987             success = true,
37988             v;
37989
37990         var root = this.getRoot(o);
37991
37992         for(var i = 0, len = root.length; i < len; i++) {
37993             var n = root[i],
37994                 values = {},
37995                 id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
37996             for(var j = 0, jlen = fields.length; j < jlen; j++) {
37997                 var f = fields.items[j],
37998                     k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
37999                 v = n[k] !== undefined ? n[k] : f.defaultValue;
38000                 v = f.convert(v, n);
38001                 values[f.name] = v;
38002             }
38003             var record = new recordType(values, id);
38004             record.json = n;
38005             records[records.length] = record;
38006         }
38007
38008         var totalRecords = records.length;
38009
38010         if(s.totalProperty) {
38011             v = parseInt(this.getTotal(o), 10);
38012             if(!isNaN(v)) {
38013                 totalRecords = v;
38014             }
38015         }
38016         if(s.successProperty){
38017             v = this.getSuccess(o);
38018             if(v === false || v === 'false'){
38019                 success = false;
38020             }
38021         }
38022
38023         return {
38024             success : success,
38025             records : records,
38026             totalRecords : totalRecords
38027         };
38028     }
38029 });/**
38030  * @class Ext.data.ArrayStore
38031  * @extends Ext.data.Store
38032  * <p>Formerly known as "SimpleStore".</p>
38033  * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
38034  * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
38035  * <p>A store configuration would be something like:<pre><code>
38036 var store = new Ext.data.ArrayStore({
38037     // store configs
38038     autoDestroy: true,
38039     storeId: 'myStore',
38040     // reader configs
38041     idIndex: 0,  
38042     fields: [
38043        'company',
38044        {name: 'price', type: 'float'},
38045        {name: 'change', type: 'float'},
38046        {name: 'pctChange', type: 'float'},
38047        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
38048     ]
38049 });
38050  * </code></pre></p>
38051  * <p>This store is configured to consume a returned object of the form:<pre><code>
38052 var myData = [
38053     ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
38054     ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
38055     ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
38056     ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
38057     ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
38058 ];
38059  * </code></pre>
38060  * An object literal of this form could also be used as the {@link #data} config option.</p>
38061  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
38062  * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
38063  * @constructor
38064  * @param {Object} config
38065  * @xtype arraystore
38066  */
38067 Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
38068     /**
38069      * @cfg {Ext.data.DataReader} reader @hide
38070      */
38071     constructor: function(config){
38072         Ext.data.ArrayStore.superclass.constructor.call(this, Ext.apply(config, {
38073             reader: new Ext.data.ArrayReader(config)
38074         }));
38075     },
38076
38077     loadData : function(data, append){
38078         if(this.expandData === true){
38079             var r = [];
38080             for(var i = 0, len = data.length; i < len; i++){
38081                 r[r.length] = [data[i]];
38082             }
38083             data = r;
38084         }
38085         Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
38086     }
38087 });
38088 Ext.reg('arraystore', Ext.data.ArrayStore);
38089
38090 // backwards compat
38091 Ext.data.SimpleStore = Ext.data.ArrayStore;
38092 Ext.reg('simplestore', Ext.data.SimpleStore);/**
38093  * @class Ext.data.JsonStore
38094  * @extends Ext.data.Store
38095  * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
38096  * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
38097  * <p>A store configuration would be something like:<pre><code>
38098 var store = new Ext.data.JsonStore({
38099     // store configs
38100     autoDestroy: true,
38101     url: 'get-images.php',
38102     storeId: 'myStore',
38103     // reader configs
38104     root: 'images',
38105     idProperty: 'name',
38106     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
38107 });
38108  * </code></pre></p>
38109  * <p>This store is configured to consume a returned object of the form:<pre><code>
38110 {
38111     images: [
38112         {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
38113         {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
38114     ]
38115 }
38116  * </code></pre>
38117  * An object literal of this form could also be used as the {@link #data} config option.</p>
38118  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
38119  * <b>{@link Ext.data.JsonReader JsonReader}</b>.</p>
38120  * @constructor
38121  * @param {Object} config
38122  * @xtype jsonstore
38123  */
38124 Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
38125     /**
38126      * @cfg {Ext.data.DataReader} reader @hide
38127      */
38128     constructor: function(config){
38129         Ext.data.JsonStore.superclass.constructor.call(this, Ext.apply(config, {
38130             reader: new Ext.data.JsonReader(config)
38131         }));
38132     }
38133 });
38134 Ext.reg('jsonstore', Ext.data.JsonStore);/**
38135  * @class Ext.data.XmlWriter
38136  * @extends Ext.data.DataWriter
38137  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action via XML.
38138  * XmlWriter uses an instance of {@link Ext.XTemplate} for maximum flexibility in defining your own custom XML schema if the default schema is not appropriate for your needs.
38139  * See the {@link #tpl} configuration-property.
38140  */
38141 Ext.data.XmlWriter = function(params) {
38142     Ext.data.XmlWriter.superclass.constructor.apply(this, arguments);
38143     // compile the XTemplate for rendering XML documents.
38144     this.tpl = (typeof(this.tpl) === 'string') ? new Ext.XTemplate(this.tpl).compile() : this.tpl.compile();
38145 };
38146 Ext.extend(Ext.data.XmlWriter, Ext.data.DataWriter, {
38147     /**
38148      * @cfg {String} documentRoot [xrequest] (Optional) The name of the XML document root-node.  <b>Note:</b>
38149      * this parameter is required </b>only when</b> sending extra {@link Ext.data.Store#baseParams baseParams} to the server
38150      * during a write-request -- if no baseParams are set, the {@link Ext.data.XmlReader#record} meta-property can
38151      * suffice as the XML document root-node for write-actions involving just a <b>single record</b>.  For requests
38152      * involving <b>multiple</b> records and <b>NO</b> baseParams, the {@link Ext.data.XmlWriter#root} property can
38153      * act as the XML document root.
38154      */
38155     documentRoot: 'xrequest',
38156     /**
38157      * @cfg {Boolean} forceDocumentRoot [false] Set to <tt>true</tt> to force XML documents having a root-node as defined
38158      * by {@link #documentRoot}, even with no baseParams defined.
38159      */
38160     forceDocumentRoot: false,
38161     /**
38162      * @cfg {String} root [records] The name of the containing element which will contain the nodes of an write-action involving <b>multiple</b> records.  Each
38163      * xml-record written to the server will be wrapped in an element named after {@link Ext.data.XmlReader#record} property.
38164      * eg:
38165 <code><pre>
38166 &lt;?xml version="1.0" encoding="UTF-8"?>
38167 &lt;user>&lt;first>Barney&lt;/first>&lt;/user>
38168 </code></pre>
38169      * However, when <b>multiple</b> records are written in a batch-operation, these records must be wrapped in a containing
38170      * Element.
38171      * eg:
38172 <code><pre>
38173 &lt;?xml version="1.0" encoding="UTF-8"?>
38174     &lt;records>
38175         &lt;first>Barney&lt;/first>&lt;/user>
38176         &lt;records>&lt;first>Barney&lt;/first>&lt;/user>
38177     &lt;/records>
38178 </code></pre>
38179      * Defaults to <tt>records</tt>.  Do not confuse the nature of this property with that of {@link #documentRoot}
38180      */
38181     root: 'records',
38182     /**
38183      * @cfg {String} xmlVersion [1.0] The <tt>version</tt> written to header of xml documents.
38184 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
38185      */
38186     xmlVersion : '1.0',
38187     /**
38188      * @cfg {String} xmlEncoding [ISO-8859-15] The <tt>encoding</tt> written to header of xml documents.
38189 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
38190      */
38191     xmlEncoding: 'ISO-8859-15',
38192     /**
38193      * @cfg {String/Ext.XTemplate} tpl The XML template used to render {@link Ext.data.Api#actions write-actions} to your server.
38194      * <p>One can easily provide his/her own custom {@link Ext.XTemplate#constructor template-definition} if the default does not suffice.</p>
38195      * <p>Defaults to:</p>
38196 <code><pre>
38197 &lt;?xml version="{version}" encoding="{encoding}"?>
38198     &lt;tpl if="documentRoot">&lt;{documentRoot}>
38199     &lt;tpl for="baseParams">
38200         &lt;tpl for=".">
38201             &lt;{name}>{value}&lt;/{name}>
38202         &lt;/tpl>
38203     &lt;/tpl>
38204     &lt;tpl if="records.length &gt; 1">&lt;{root}>',
38205     &lt;tpl for="records">
38206         &lt;{parent.record}>
38207         &lt;tpl for=".">
38208             &lt;{name}>{value}&lt;/{name}>
38209         &lt;/tpl>
38210         &lt;/{parent.record}>
38211     &lt;/tpl>
38212     &lt;tpl if="records.length &gt; 1">&lt;/{root}>&lt;/tpl>
38213     &lt;tpl if="documentRoot">&lt;/{documentRoot}>&lt;/tpl>
38214 </pre></code>
38215      * <p>Templates will be called with the following API</p>
38216      * <ul>
38217      * <li>{String} version [1.0] The xml version.</li>
38218      * <li>{String} encoding [ISO-8859-15] The xml encoding.</li>
38219      * <li>{String/false} documentRoot The XML document root-node name or <tt>false</tt> if not required.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
38220      * <li>{String} record The meta-data parameter defined on your {@link Ext.data.XmlReader#record} configuration represents the name of the xml-tag containing each record.</li>
38221      * <li>{String} root The meta-data parameter defined by {@link Ext.data.XmlWriter#root} configuration-parameter.  Represents the name of the xml root-tag when sending <b>multiple</b> records to the server.</li>
38222      * <li>{Array} records The records being sent to the server, ie: the subject of the write-action being performed.  The records parameter will be always be an array, even when only a single record is being acted upon.
38223      *     Each item within the records array will contain an array of field objects having the following properties:
38224      *     <ul>
38225      *         <li>{String} name The field-name of the record as defined by your {@link Ext.data.Record#create Ext.data.Record definition}.  The "mapping" property will be used, otherwise it will match the "name" property.  Use this parameter to define the XML tag-name of the property.</li>
38226      *         <li>{Mixed} value The record value of the field enclosed within XML tags specified by name property above.</li>
38227      *     </ul></li>
38228      * <li>{Array} baseParams.  The baseParams as defined upon {@link Ext.data.Store#baseParams}.  Note that the baseParams have been converted into an array of [{name : "foo", value: "bar"}, ...] pairs in the same manner as the <b>records</b> parameter above.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
38229      * </ul>
38230      */
38231     // Encoding the ? here in case it's being included by some kind of page that will parse it (eg. PHP)
38232     tpl: '<tpl for="."><\u003fxml version="{version}" encoding="{encoding}"\u003f><tpl if="documentRoot"><{documentRoot}><tpl for="baseParams"><tpl for="."><{name}>{value}</{name}</tpl></tpl></tpl><tpl if="records.length&gt;1"><{root}></tpl><tpl for="records"><{parent.record}><tpl for="."><{name}>{value}</{name}></tpl></{parent.record}></tpl><tpl if="records.length&gt;1"></{root}></tpl><tpl if="documentRoot"></{documentRoot}></tpl></tpl>',
38233
38234
38235     /**
38236      * XmlWriter implementation of the final stage of a write action.
38237      * @param {Object} params Transport-proxy's (eg: {@link Ext.Ajax#request}) params-object to write-to.
38238      * @param {Object} baseParams as defined by {@link Ext.data.Store#baseParams}.  The baseParms must be encoded by the extending class, eg: {@link Ext.data.JsonWriter}, {@link Ext.data.XmlWriter}.
38239      * @param {Object/Object[]} data Data-object representing the compiled Store-recordset.
38240      */
38241     render : function(params, baseParams, data) {
38242         baseParams = this.toArray(baseParams);
38243         params.xmlData = this.tpl.applyTemplate({
38244             version: this.xmlVersion,
38245             encoding: this.xmlEncoding,
38246             documentRoot: (baseParams.length > 0 || this.forceDocumentRoot === true) ? this.documentRoot : false,
38247             record: this.meta.record,
38248             root: this.root,
38249             baseParams: baseParams,
38250             records: (Ext.isArray(data[0])) ? data : [data]
38251         });
38252     },
38253
38254     /**
38255      * createRecord
38256      * @protected
38257      * @param {Ext.data.Record} rec
38258      * @return {Array} Array of <tt>name:value</tt> pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
38259      */
38260     createRecord : function(rec) {
38261         return this.toArray(this.toHash(rec));
38262     },
38263
38264     /**
38265      * updateRecord
38266      * @protected
38267      * @param {Ext.data.Record} rec
38268      * @return {Array} Array of {name:value} pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
38269      */
38270     updateRecord : function(rec) {
38271         return this.toArray(this.toHash(rec));
38272
38273     },
38274     /**
38275      * destroyRecord
38276      * @protected
38277      * @param {Ext.data.Record} rec
38278      * @return {Array} Array containing a attribute-object (name/value pair) representing the {@link Ext.data.DataReader#idProperty idProperty}.
38279      */
38280     destroyRecord : function(rec) {
38281         var data = {};
38282         data[this.meta.idProperty] = rec.id;
38283         return this.toArray(data);
38284     }
38285 });
38286 /**
38287  * @class Ext.data.XmlReader
38288  * @extends Ext.data.DataReader
38289  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
38290  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
38291  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
38292  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
38293  * <p>Example code:</p>
38294  * <pre><code>
38295 var Employee = Ext.data.Record.create([
38296    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
38297    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
38298 ]);
38299 var myReader = new Ext.data.XmlReader({
38300    totalProperty: "results", // The element which contains the total dataset size (optional)
38301    record: "row",           // The repeated element which contains row information
38302    idProperty: "id"         // The element within the row that provides an ID for the record (optional)
38303    messageProperty: "msg"   // The element within the response that provides a user-feedback message (optional)
38304 }, Employee);
38305 </code></pre>
38306  * <p>
38307  * This would consume an XML file like this:
38308  * <pre><code>
38309 &lt;?xml version="1.0" encoding="UTF-8"?>
38310 &lt;dataset>
38311  &lt;results>2&lt;/results>
38312  &lt;row>
38313    &lt;id>1&lt;/id>
38314    &lt;name>Bill&lt;/name>
38315    &lt;occupation>Gardener&lt;/occupation>
38316  &lt;/row>
38317  &lt;row>
38318    &lt;id>2&lt;/id>
38319    &lt;name>Ben&lt;/name>
38320    &lt;occupation>Horticulturalist&lt;/occupation>
38321  &lt;/row>
38322 &lt;/dataset>
38323 </code></pre>
38324  * @cfg {String} totalProperty The DomQuery path from which to retrieve the total number of records
38325  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
38326  * paged from the remote server.
38327  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
38328  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
38329  * @cfg {String} successProperty The DomQuery path to the success attribute used by forms.
38330  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
38331  * a record identifier value.
38332  * @constructor
38333  * Create a new XmlReader.
38334  * @param {Object} meta Metadata configuration options
38335  * @param {Object} recordType Either an Array of field definition objects as passed to
38336  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
38337  */
38338 Ext.data.XmlReader = function(meta, recordType){
38339     meta = meta || {};
38340
38341     // backwards compat, convert idPath or id / success
38342     Ext.applyIf(meta, {
38343         idProperty: meta.idProperty || meta.idPath || meta.id,
38344         successProperty: meta.successProperty || meta.success
38345     });
38346
38347     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
38348 };
38349 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
38350     /**
38351      * This method is only used by a DataProxy which has retrieved data from a remote server.
38352      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
38353      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
38354      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
38355      * a cache of Ext.data.Records.
38356      */
38357     read : function(response){
38358         var doc = response.responseXML;
38359         if(!doc) {
38360             throw {message: "XmlReader.read: XML Document not available"};
38361         }
38362         return this.readRecords(doc);
38363     },
38364
38365     /**
38366      * Create a data block containing Ext.data.Records from an XML document.
38367      * @param {Object} doc A parsed XML document.
38368      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
38369      * a cache of Ext.data.Records.
38370      */
38371     readRecords : function(doc){
38372         /**
38373          * After any data loads/reads, the raw XML Document is available for further custom processing.
38374          * @type XMLDocument
38375          */
38376         this.xmlData = doc;
38377
38378         var root    = doc.documentElement || doc,
38379             q       = Ext.DomQuery,
38380             totalRecords = 0,
38381             success = true;
38382
38383         if(this.meta.totalProperty){
38384             totalRecords = this.getTotal(root, 0);
38385         }
38386         if(this.meta.successProperty){
38387             success = this.getSuccess(root);
38388         }
38389
38390         var records = this.extractData(q.select(this.meta.record, root), true); // <-- true to return Ext.data.Record[]
38391
38392         // TODO return Ext.data.Response instance.  @see #readResponse
38393         return {
38394             success : success,
38395             records : records,
38396             totalRecords : totalRecords || records.length
38397         };
38398     },
38399
38400     /**
38401      * Decode an XML response from server.
38402      * @param {String} action [{@link Ext.data.Api#actions} create|read|update|destroy]
38403      * @param {Object} response HTTP Response object from browser.
38404      * @return {Ext.data.Response} An instance of {@link Ext.data.Response}
38405      */
38406     readResponse : function(action, response) {
38407         var q   = Ext.DomQuery,
38408         doc     = response.responseXML;
38409
38410         // create general Response instance.
38411         var res = new Ext.data.Response({
38412             action: action,
38413             success : this.getSuccess(doc),
38414             message: this.getMessage(doc),
38415             data: this.extractData(q.select(this.meta.record, doc) || q.select(this.meta.root, doc), false),
38416             raw: doc
38417         });
38418
38419         if (Ext.isEmpty(res.success)) {
38420             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
38421         }
38422
38423         // Create actions from a response having status 200 must return pk
38424         if (action === Ext.data.Api.actions.create) {
38425             var def = Ext.isDefined(res.data);
38426             if (def && Ext.isEmpty(res.data)) {
38427                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
38428             }
38429             else if (!def) {
38430                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
38431             }
38432         }
38433         return res;
38434     },
38435
38436     getSuccess : function() {
38437         return true;
38438     },
38439
38440     /**
38441      * build response-data extractor functions.
38442      * @private
38443      * @ignore
38444      */
38445     buildExtractors : function() {
38446         if(this.ef){
38447             return;
38448         }
38449         var s       = this.meta,
38450             Record  = this.recordType,
38451             f       = Record.prototype.fields,
38452             fi      = f.items,
38453             fl      = f.length;
38454
38455         if(s.totalProperty) {
38456             this.getTotal = this.createAccessor(s.totalProperty);
38457         }
38458         if(s.successProperty) {
38459             this.getSuccess = this.createAccessor(s.successProperty);
38460         }
38461         if (s.messageProperty) {
38462             this.getMessage = this.createAccessor(s.messageProperty);
38463         }
38464         this.getRoot = function(res) {
38465             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
38466         };
38467         if (s.idPath || s.idProperty) {
38468             var g = this.createAccessor(s.idPath || s.idProperty);
38469             this.getId = function(rec) {
38470                 var id = g(rec) || rec.id;
38471                 return (id === undefined || id === '') ? null : id;
38472             };
38473         } else {
38474             this.getId = function(){return null;};
38475         }
38476         var ef = [];
38477         for(var i = 0; i < fl; i++){
38478             f = fi[i];
38479             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
38480             ef.push(this.createAccessor(map));
38481         }
38482         this.ef = ef;
38483     },
38484
38485     /**
38486      * Creates a function to return some particular key of data from a response.
38487      * @param {String} key
38488      * @return {Function}
38489      * @private
38490      * @ignore
38491      */
38492     createAccessor : function(){
38493         var q = Ext.DomQuery;
38494         return function(key) {
38495             switch(key) {
38496                 case this.meta.totalProperty:
38497                     return function(root, def){
38498                         return q.selectNumber(key, root, def);
38499                     };
38500                     break;
38501                 case this.meta.successProperty:
38502                     return function(root, def) {
38503                         var sv = q.selectValue(key, root, true);
38504                         var success = sv !== false && sv !== 'false';
38505                         return success;
38506                     };
38507                     break;
38508                 default:
38509                     return function(root, def) {
38510                         return q.selectValue(key, root, def);
38511                     };
38512                     break;
38513             }
38514         };
38515     }(),
38516
38517     /**
38518      * extracts values and type-casts a row of data from server, extracted by #extractData
38519      * @param {Hash} data
38520      * @param {Ext.data.Field[]} items
38521      * @param {Number} len
38522      * @private
38523      * @ignore
38524      */
38525     extractValues : function(data, items, len) {
38526         var f, values = {};
38527         for(var j = 0; j < len; j++){
38528             f = items[j];
38529             var v = this.ef[j](data);
38530             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
38531         }
38532         return values;
38533     }
38534 });/**
38535  * @class Ext.data.XmlStore
38536  * @extends Ext.data.Store
38537  * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.
38538  * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>
38539  * <p>A store configuration would be something like:<pre><code>
38540 var store = new Ext.data.XmlStore({
38541     // store configs
38542     autoDestroy: true,
38543     storeId: 'myStore',
38544     url: 'sheldon.xml', // automatically configures a HttpProxy
38545     // reader configs
38546     record: 'Item', // records will have an "Item" tag
38547     idPath: 'ASIN',
38548     totalRecords: '@TotalResults'
38549     fields: [
38550         // set up the fields mapping into the xml doc
38551         // The first needs mapping, the others are very basic
38552         {name: 'Author', mapping: 'ItemAttributes > Author'},
38553         'Title', 'Manufacturer', 'ProductGroup'
38554     ]
38555 });
38556  * </code></pre></p>
38557  * <p>This store is configured to consume a returned object of the form:<pre><code>
38558 &#60?xml version="1.0" encoding="UTF-8"?>
38559 &#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">
38560     &#60Items>
38561         &#60Request>
38562             &#60IsValid>True&#60/IsValid>
38563             &#60ItemSearchRequest>
38564                 &#60Author>Sidney Sheldon&#60/Author>
38565                 &#60SearchIndex>Books&#60/SearchIndex>
38566             &#60/ItemSearchRequest>
38567         &#60/Request>
38568         &#60TotalResults>203&#60/TotalResults>
38569         &#60TotalPages>21&#60/TotalPages>
38570         &#60Item>
38571             &#60ASIN>0446355453&#60/ASIN>
38572             &#60DetailPageURL>
38573                 http://www.amazon.com/
38574             &#60/DetailPageURL>
38575             &#60ItemAttributes>
38576                 &#60Author>Sidney Sheldon&#60/Author>
38577                 &#60Manufacturer>Warner Books&#60/Manufacturer>
38578                 &#60ProductGroup>Book&#60/ProductGroup>
38579                 &#60Title>Master of the Game&#60/Title>
38580             &#60/ItemAttributes>
38581         &#60/Item>
38582     &#60/Items>
38583 &#60/ItemSearchResponse>
38584  * </code></pre>
38585  * An object literal of this form could also be used as the {@link #data} config option.</p>
38586  * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of 
38587  * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>
38588  * @constructor
38589  * @param {Object} config
38590  * @xtype xmlstore
38591  */
38592 Ext.data.XmlStore = Ext.extend(Ext.data.Store, {
38593     /**
38594      * @cfg {Ext.data.DataReader} reader @hide
38595      */
38596     constructor: function(config){
38597         Ext.data.XmlStore.superclass.constructor.call(this, Ext.apply(config, {
38598             reader: new Ext.data.XmlReader(config)
38599         }));
38600     }
38601 });
38602 Ext.reg('xmlstore', Ext.data.XmlStore);/**
38603  * @class Ext.data.GroupingStore
38604  * @extends Ext.data.Store
38605  * A specialized store implementation that provides for grouping records by one of the available fields. This
38606  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to provide the data model for
38607  * a grouped GridPanel.
38608  *
38609  * Internally, GroupingStore is simply a normal Store with multi sorting enabled from the start. The grouping field
38610  * and direction are always injected as the first sorter pair. GroupingView picks up on the configured groupField and
38611  * builds grid rows appropriately.
38612  *
38613  * @constructor
38614  * Creates a new GroupingStore.
38615  * @param {Object} config A config object containing the objects needed for the Store to access data,
38616  * and read the data into Records.
38617  * @xtype groupingstore
38618  */
38619 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {
38620
38621     //inherit docs
38622     constructor: function(config) {
38623         config = config || {};
38624
38625         //We do some preprocessing here to massage the grouping + sorting options into a single
38626         //multi sort array. If grouping and sorting options are both presented to the constructor,
38627         //the sorters array consists of the grouping sorter object followed by the sorting sorter object
38628         //see Ext.data.Store's sorting functions for details about how multi sorting works
38629         this.hasMultiSort  = true;
38630         this.multiSortInfo = this.multiSortInfo || {sorters: []};
38631
38632         var sorters    = this.multiSortInfo.sorters,
38633             groupField = config.groupField || this.groupField,
38634             sortInfo   = config.sortInfo || this.sortInfo,
38635             groupDir   = config.groupDir || this.groupDir;
38636
38637         //add the grouping sorter object first
38638         if(groupField){
38639             sorters.push({
38640                 field    : groupField,
38641                 direction: groupDir
38642             });
38643         }
38644
38645         //add the sorting sorter object if it is present
38646         if (sortInfo) {
38647             sorters.push(sortInfo);
38648         }
38649
38650         Ext.data.GroupingStore.superclass.constructor.call(this, config);
38651
38652         this.addEvents(
38653           /**
38654            * @event groupchange
38655            * Fired whenever a call to store.groupBy successfully changes the grouping on the store
38656            * @param {Ext.data.GroupingStore} store The grouping store
38657            * @param {String} groupField The field that the store is now grouped by
38658            */
38659           'groupchange'
38660         );
38661
38662         this.applyGroupField();
38663     },
38664
38665     /**
38666      * @cfg {String} groupField
38667      * The field name by which to sort the store's data (defaults to '').
38668      */
38669     /**
38670      * @cfg {Boolean} remoteGroup
38671      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the
38672      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a
38673      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.
38674      */
38675     remoteGroup : false,
38676     /**
38677      * @cfg {Boolean} groupOnSort
38678      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the
38679      * existing sort info (defaults to false).
38680      */
38681     groupOnSort:false,
38682
38683     groupDir : 'ASC',
38684
38685     /**
38686      * Clears any existing grouping and refreshes the data using the default sort.
38687      */
38688     clearGrouping : function(){
38689         this.groupField = false;
38690
38691         if(this.remoteGroup){
38692             if(this.baseParams){
38693                 delete this.baseParams.groupBy;
38694                 delete this.baseParams.groupDir;
38695             }
38696             var lo = this.lastOptions;
38697             if(lo && lo.params){
38698                 delete lo.params.groupBy;
38699                 delete lo.params.groupDir;
38700             }
38701
38702             this.reload();
38703         }else{
38704             this.sort();
38705             this.fireEvent('datachanged', this);
38706         }
38707     },
38708
38709     /**
38710      * Groups the data by the specified field.
38711      * @param {String} field The field name by which to sort the store's data
38712      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed
38713      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)
38714      */
38715     groupBy : function(field, forceRegroup, direction) {
38716         direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
38717
38718         if (this.groupField == field && this.groupDir == direction && !forceRegroup) {
38719             return; // already grouped by this field
38720         }
38721
38722         //check the contents of the first sorter. If the field matches the CURRENT groupField (before it is set to the new one),
38723         //remove the sorter as it is actually the grouper. The new grouper is added back in by this.sort
38724         sorters = this.multiSortInfo.sorters;
38725         if (sorters.length > 0 && sorters[0].field == this.groupField) {
38726             sorters.shift();
38727         }
38728
38729         this.groupField = field;
38730         this.groupDir = direction;
38731         this.applyGroupField();
38732
38733         var fireGroupEvent = function() {
38734             this.fireEvent('groupchange', this, this.getGroupState());
38735         };
38736
38737         if (this.groupOnSort) {
38738             this.sort(field, direction);
38739             fireGroupEvent.call(this);
38740             return;
38741         }
38742
38743         if (this.remoteGroup) {
38744             this.on('load', fireGroupEvent, this, {single: true});
38745             this.reload();
38746         } else {
38747             this.sort(sorters);
38748             fireGroupEvent.call(this);
38749         }
38750     },
38751
38752     //GroupingStore always uses multisorting so we intercept calls to sort here to make sure that our grouping sorter object
38753     //is always injected first.
38754     sort : function(fieldName, dir) {
38755         if (this.remoteSort) {
38756             return Ext.data.GroupingStore.superclass.sort.call(this, fieldName, dir);
38757         }
38758
38759         var sorters = [];
38760
38761         //cater for any existing valid arguments to this.sort, massage them into an array of sorter objects
38762         if (Ext.isArray(arguments[0])) {
38763             sorters = arguments[0];
38764         } else if (fieldName == undefined) {
38765             //we preserve the existing sortInfo here because this.sort is called after
38766             //clearGrouping and there may be existing sorting
38767             sorters = [this.sortInfo];
38768         } else {
38769             //TODO: this is lifted straight from Ext.data.Store's singleSort function. It should instead be
38770             //refactored into a common method if possible
38771             var field = this.fields.get(fieldName);
38772             if (!field) return false;
38773
38774             var name       = field.name,
38775                 sortInfo   = this.sortInfo || null,
38776                 sortToggle = this.sortToggle ? this.sortToggle[name] : null;
38777
38778             if (!dir) {
38779                 if (sortInfo && sortInfo.field == name) { // toggle sort dir
38780                     dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
38781                 } else {
38782                     dir = field.sortDir;
38783                 }
38784             }
38785
38786             this.sortToggle[name] = dir;
38787             this.sortInfo = {field: name, direction: dir};
38788
38789             sorters = [this.sortInfo];
38790         }
38791
38792         //add the grouping sorter object as the first multisort sorter
38793         if (this.groupField) {
38794             sorters.unshift({direction: this.groupDir, field: this.groupField});
38795         }
38796
38797         return this.multiSort.call(this, sorters, dir);
38798     },
38799
38800     /**
38801      * @private
38802      * Saves the current grouping field and direction to this.baseParams and this.lastOptions.params
38803      * if we're using remote grouping. Does not actually perform any grouping - just stores values
38804      */
38805     applyGroupField: function(){
38806         if (this.remoteGroup) {
38807             if(!this.baseParams){
38808                 this.baseParams = {};
38809             }
38810
38811             Ext.apply(this.baseParams, {
38812                 groupBy : this.groupField,
38813                 groupDir: this.groupDir
38814             });
38815
38816             var lo = this.lastOptions;
38817             if (lo && lo.params) {
38818                 lo.params.groupDir = this.groupDir;
38819
38820                 //this is deleted because of a bug reported at http://www.extjs.com/forum/showthread.php?t=82907
38821                 delete lo.params.groupBy;
38822             }
38823         }
38824     },
38825
38826     /**
38827      * @private
38828      * TODO: This function is apparently never invoked anywhere in the framework. It has no documentation
38829      * and should be considered for deletion
38830      */
38831     applyGrouping : function(alwaysFireChange){
38832         if(this.groupField !== false){
38833             this.groupBy(this.groupField, true, this.groupDir);
38834             return true;
38835         }else{
38836             if(alwaysFireChange === true){
38837                 this.fireEvent('datachanged', this);
38838             }
38839             return false;
38840         }
38841     },
38842
38843     /**
38844      * @private
38845      * Returns the grouping field that should be used. If groupOnSort is used this will be sortInfo's field,
38846      * otherwise it will be this.groupField
38847      * @return {String} The group field
38848      */
38849     getGroupState : function(){
38850         return this.groupOnSort && this.groupField !== false ?
38851                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;
38852     }
38853 });
38854 Ext.reg('groupingstore', Ext.data.GroupingStore);
38855 /**
38856  * @class Ext.data.DirectProxy
38857  * @extends Ext.data.DataProxy
38858  */
38859 Ext.data.DirectProxy = function(config){
38860     Ext.apply(this, config);
38861     if(typeof this.paramOrder == 'string'){
38862         this.paramOrder = this.paramOrder.split(/[\s,|]/);
38863     }
38864     Ext.data.DirectProxy.superclass.constructor.call(this, config);
38865 };
38866
38867 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {
38868     /**
38869      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed
38870      * server side.  Specify the params in the order in which they must be executed on the server-side
38871      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
38872      * comma, or pipe. For example,
38873      * any of the following would be acceptable:<pre><code>
38874 paramOrder: ['param1','param2','param3']
38875 paramOrder: 'param1 param2 param3'
38876 paramOrder: 'param1,param2,param3'
38877 paramOrder: 'param1|param2|param'
38878      </code></pre>
38879      */
38880     paramOrder: undefined,
38881
38882     /**
38883      * @cfg {Boolean} paramsAsHash
38884      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a
38885      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
38886      */
38887     paramsAsHash: true,
38888
38889     /**
38890      * @cfg {Function} directFn
38891      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter
38892      * for Store's which will not implement a full CRUD api.
38893      */
38894     directFn : undefined,
38895
38896     /**
38897      * DirectProxy implementation of {@link Ext.data.DataProxy#doRequest}
38898      * @param {String} action The crud action type (create, read, update, destroy)
38899      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
38900      * @param {Object} params An object containing properties which are to be used as HTTP parameters
38901      * for the request to the remote server.
38902      * @param {Ext.data.DataReader} reader The Reader object which converts the data
38903      * object into a block of Ext.data.Records.
38904      * @param {Function} callback
38905      * <div class="sub-desc"><p>A function to be called after the request.
38906      * The <tt>callback</tt> is passed the following arguments:<ul>
38907      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
38908      * <li><tt>options</tt>: Options object from the action request</li>
38909      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
38910      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
38911      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
38912      * @protected
38913      */
38914     doRequest : function(action, rs, params, reader, callback, scope, options) {
38915         var args = [],
38916             directFn = this.api[action] || this.directFn;
38917
38918         switch (action) {
38919             case Ext.data.Api.actions.create:
38920                 args.push(params.jsonData);             // <-- create(Hash)
38921                 break;
38922             case Ext.data.Api.actions.read:
38923                 // If the method has no parameters, ignore the paramOrder/paramsAsHash.
38924                 if(directFn.directCfg.method.len > 0){
38925                     if(this.paramOrder){
38926                         for(var i = 0, len = this.paramOrder.length; i < len; i++){
38927                             args.push(params[this.paramOrder[i]]);
38928                         }
38929                     }else if(this.paramsAsHash){
38930                         args.push(params);
38931                     }
38932                 }
38933                 break;
38934             case Ext.data.Api.actions.update:
38935                 args.push(params.jsonData);        // <-- update(Hash/Hash[])
38936                 break;
38937             case Ext.data.Api.actions.destroy:
38938                 args.push(params.jsonData);        // <-- destroy(Int/Int[])
38939                 break;
38940         }
38941
38942         var trans = {
38943             params : params || {},
38944             request: {
38945                 callback : callback,
38946                 scope : scope,
38947                 arg : options
38948             },
38949             reader: reader
38950         };
38951
38952         args.push(this.createCallback(action, rs, trans), this);
38953         directFn.apply(window, args);
38954     },
38955
38956     // private
38957     createCallback : function(action, rs, trans) {
38958         var me = this;
38959         return function(result, res) {
38960             if (!res.status) {
38961                 // @deprecated fire loadexception
38962                 if (action === Ext.data.Api.actions.read) {
38963                     me.fireEvent("loadexception", me, trans, res, null);
38964                 }
38965                 me.fireEvent('exception', me, 'remote', action, trans, res, null);
38966                 trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);
38967                 return;
38968             }
38969             if (action === Ext.data.Api.actions.read) {
38970                 me.onRead(action, trans, result, res);
38971             } else {
38972                 me.onWrite(action, trans, result, res, rs);
38973             }
38974         };
38975     },
38976
38977     /**
38978      * Callback for read actions
38979      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
38980      * @param {Object} trans The request transaction object
38981      * @param {Object} result Data object picked out of the server-response.
38982      * @param {Object} res The server response
38983      * @protected
38984      */
38985     onRead : function(action, trans, result, res) {
38986         var records;
38987         try {
38988             records = trans.reader.readRecords(result);
38989         }
38990         catch (ex) {
38991             // @deprecated: Fire old loadexception for backwards-compat.
38992             this.fireEvent("loadexception", this, trans, res, ex);
38993
38994             this.fireEvent('exception', this, 'response', action, trans, res, ex);
38995             trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);
38996             return;
38997         }
38998         this.fireEvent("load", this, res, trans.request.arg);
38999         trans.request.callback.call(trans.request.scope, records, trans.request.arg, true);
39000     },
39001     /**
39002      * Callback for write actions
39003      * @param {String} action [{@link Ext.data.Api#actions create|read|update|destroy}]
39004      * @param {Object} trans The request transaction object
39005      * @param {Object} result Data object picked out of the server-response.
39006      * @param {Object} res The server response
39007      * @param {Ext.data.Record/[Ext.data.Record]} rs The Store resultset associated with the action.
39008      * @protected
39009      */
39010     onWrite : function(action, trans, result, res, rs) {
39011         var data = trans.reader.extractData(trans.reader.getRoot(result), false);
39012         var success = trans.reader.getSuccess(result);
39013         success = (success !== false);
39014         if (success){
39015             this.fireEvent("write", this, action, data, res, rs, trans.request.arg);
39016         }else{
39017             this.fireEvent('exception', this, 'remote', action, trans, result, rs);
39018         }
39019         trans.request.callback.call(trans.request.scope, data, res, success);
39020     }
39021 });
39022 /**
39023  * @class Ext.data.DirectStore
39024  * @extends Ext.data.Store
39025  * <p>Small helper class to create an {@link Ext.data.Store} configured with an
39026  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting
39027  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.
39028  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}
39029  * configured as needed.</p>
39030  *
39031  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>
39032  * <div><ul class="mdetail-params">
39033  * <li><b>{@link Ext.data.Store Store}</b></li>
39034  * <div class="sub-desc"><ul class="mdetail-params">
39035  *
39036  * </ul></div>
39037  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>
39038  * <div class="sub-desc"><ul class="mdetail-params">
39039  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>
39040  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>
39041  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>
39042  * </ul></div>
39043  *
39044  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>
39045  * <div class="sub-desc"><ul class="mdetail-params">
39046  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>
39047  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>
39048  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>
39049  * </ul></div>
39050  * </ul></div>
39051  *
39052  * @xtype directstore
39053  *
39054  * @constructor
39055  * @param {Object} config
39056  */
39057 Ext.data.DirectStore = Ext.extend(Ext.data.Store, {
39058     constructor : function(config){
39059         // each transaction upon a singe record will generate a distinct Direct transaction since Direct queues them into one Ajax request.
39060         var c = Ext.apply({}, {
39061             batchTransactions: false
39062         }, config);
39063         Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {
39064             proxy: Ext.isDefined(c.proxy) ? c.proxy : new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')),
39065             reader: (!Ext.isDefined(c.reader) && c.fields) ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader
39066         }));
39067     }
39068 });
39069 Ext.reg('directstore', Ext.data.DirectStore);
39070 /**
39071  * @class Ext.Direct
39072  * @extends Ext.util.Observable
39073  * <p><b><u>Overview</u></b></p>
39074  *
39075  * <p>Ext.Direct aims to streamline communication between the client and server
39076  * by providing a single interface that reduces the amount of common code
39077  * typically required to validate data and handle returned data packets
39078  * (reading data, error conditions, etc).</p>
39079  *
39080  * <p>The Ext.direct namespace includes several classes for a closer integration
39081  * with the server-side. The Ext.data namespace also includes classes for working
39082  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>
39083  *
39084  * <p><b><u>Specification</u></b></p>
39085  *
39086  * <p>For additional information consult the
39087  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>
39088  *
39089  * <p><b><u>Providers</u></b></p>
39090  *
39091  * <p>Ext.Direct uses a provider architecture, where one or more providers are
39092  * used to transport data to and from the server. There are several providers
39093  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>
39094  *
39095  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>
39096  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>
39097  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side
39098  * on the client.</li>
39099  * </ul></div>
39100  *
39101  * <p>A provider does not need to be invoked directly, providers are added via
39102  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>
39103  *
39104  * <p><b><u>Router</u></b></p>
39105  *
39106  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client
39107  * to the appropriate server-side method. Because the Ext.Direct API is completely
39108  * platform-agnostic, you could completely swap out a Java based server solution
39109  * and replace it with one that uses C# without changing the client side JavaScript
39110  * at all.</p>
39111  *
39112  * <p><b><u>Server side events</u></b></p>
39113  *
39114  * <p>Custom events from the server may be handled by the client by adding
39115  * listeners, for example:</p>
39116  * <pre><code>
39117 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}
39118
39119 // add a handler for a 'message' event sent by the server
39120 Ext.Direct.on('message', function(e){
39121     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));
39122             out.el.scrollTo('t', 100000, true);
39123 });
39124  * </code></pre>
39125  * @singleton
39126  */
39127 Ext.Direct = Ext.extend(Ext.util.Observable, {
39128     /**
39129      * Each event type implements a getData() method. The default event types are:
39130      * <div class="mdetail-params"><ul>
39131      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>
39132      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>
39133      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>
39134      * </ul></div>
39135      * @property eventTypes
39136      * @type Object
39137      */
39138
39139     /**
39140      * Four types of possible exceptions which can occur:
39141      * <div class="mdetail-params"><ul>
39142      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>
39143      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>
39144      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>
39145      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>
39146      * </ul></div>
39147      * @property exceptions
39148      * @type Object
39149      */
39150     exceptions: {
39151         TRANSPORT: 'xhr',
39152         PARSE: 'parse',
39153         LOGIN: 'login',
39154         SERVER: 'exception'
39155     },
39156
39157     // private
39158     constructor: function(){
39159         this.addEvents(
39160             /**
39161              * @event event
39162              * Fires after an event.
39163              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
39164              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39165              */
39166             'event',
39167             /**
39168              * @event exception
39169              * Fires after an event exception.
39170              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
39171              */
39172             'exception'
39173         );
39174         this.transactions = {};
39175         this.providers = {};
39176     },
39177
39178     /**
39179      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.
39180      * If the provider is not already connected, it will auto-connect.
39181      * <pre><code>
39182 var pollProv = new Ext.direct.PollingProvider({
39183     url: 'php/poll2.php'
39184 });
39185
39186 Ext.Direct.addProvider(
39187     {
39188         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider}
39189         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.
39190         "actions":{              // each property within the actions object represents a Class
39191             "TestAction":[       // array of methods within each server side Class
39192             {
39193                 "name":"doEcho", // name of method
39194                 "len":1
39195             },{
39196                 "name":"multiply",
39197                 "len":1
39198             },{
39199                 "name":"doForm",
39200                 "formHandler":true, // handle form on server with Ext.Direct.Transaction
39201                 "len":1
39202             }]
39203         },
39204         "namespace":"myApplication",// namespace to create the Remoting Provider in
39205     },{
39206         type: 'polling', // create a {@link Ext.direct.PollingProvider}
39207         url:  'php/poll.php'
39208     },
39209     pollProv // reference to previously created instance
39210 );
39211      * </code></pre>
39212      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance
39213      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each
39214      * Provider description instructs Ext.Direct how to create client-side stub methods.
39215      */
39216     addProvider : function(provider){
39217         var a = arguments;
39218         if(a.length > 1){
39219             for(var i = 0, len = a.length; i < len; i++){
39220                 this.addProvider(a[i]);
39221             }
39222             return;
39223         }
39224
39225         // if provider has not already been instantiated
39226         if(!provider.events){
39227             provider = new Ext.Direct.PROVIDERS[provider.type](provider);
39228         }
39229         provider.id = provider.id || Ext.id();
39230         this.providers[provider.id] = provider;
39231
39232         provider.on('data', this.onProviderData, this);
39233         provider.on('exception', this.onProviderException, this);
39234
39235
39236         if(!provider.isConnected()){
39237             provider.connect();
39238         }
39239
39240         return provider;
39241     },
39242
39243     /**
39244      * Retrieve a {@link Ext.direct.Provider provider} by the
39245      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is
39246      * {@link #addProvider added}.
39247      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider}
39248      */
39249     getProvider : function(id){
39250         return this.providers[id];
39251     },
39252
39253     removeProvider : function(id){
39254         var provider = id.id ? id : this.providers[id];
39255         provider.un('data', this.onProviderData, this);
39256         provider.un('exception', this.onProviderException, this);
39257         delete this.providers[provider.id];
39258         return provider;
39259     },
39260
39261     addTransaction: function(t){
39262         this.transactions[t.tid] = t;
39263         return t;
39264     },
39265
39266     removeTransaction: function(t){
39267         delete this.transactions[t.tid || t];
39268         return t;
39269     },
39270
39271     getTransaction: function(tid){
39272         return this.transactions[tid.tid || tid];
39273     },
39274
39275     onProviderData : function(provider, e){
39276         if(Ext.isArray(e)){
39277             for(var i = 0, len = e.length; i < len; i++){
39278                 this.onProviderData(provider, e[i]);
39279             }
39280             return;
39281         }
39282         if(e.name && e.name != 'event' && e.name != 'exception'){
39283             this.fireEvent(e.name, e);
39284         }else if(e.type == 'exception'){
39285             this.fireEvent('exception', e);
39286         }
39287         this.fireEvent('event', e, provider);
39288     },
39289
39290     createEvent : function(response, extraProps){
39291         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));
39292     }
39293 });
39294 // overwrite impl. with static instance
39295 Ext.Direct = new Ext.Direct();
39296
39297 Ext.Direct.TID = 1;
39298 Ext.Direct.PROVIDERS = {};/**
39299  * @class Ext.Direct.Transaction
39300  * @extends Object
39301  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>
39302  * @constructor
39303  * @param {Object} config
39304  */
39305 Ext.Direct.Transaction = function(config){
39306     Ext.apply(this, config);
39307     this.tid = ++Ext.Direct.TID;
39308     this.retryCount = 0;
39309 };
39310 Ext.Direct.Transaction.prototype = {
39311     send: function(){
39312         this.provider.queueTransaction(this);
39313     },
39314
39315     retry: function(){
39316         this.retryCount++;
39317         this.send();
39318     },
39319
39320     getProvider: function(){
39321         return this.provider;
39322     }
39323 };Ext.Direct.Event = function(config){
39324     Ext.apply(this, config);
39325 };
39326
39327 Ext.Direct.Event.prototype = {
39328     status: true,
39329     getData: function(){
39330         return this.data;
39331     }
39332 };
39333
39334 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {
39335     type: 'rpc',
39336     getTransaction: function(){
39337         return this.transaction || Ext.Direct.getTransaction(this.tid);
39338     }
39339 });
39340
39341 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {
39342     status: false,
39343     type: 'exception'
39344 });
39345
39346 Ext.Direct.eventTypes = {
39347     'rpc':  Ext.Direct.RemotingEvent,
39348     'event':  Ext.Direct.Event,
39349     'exception':  Ext.Direct.ExceptionEvent
39350 };
39351 /**
39352  * @class Ext.direct.Provider
39353  * @extends Ext.util.Observable
39354  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>
39355  * 
39356  * <p>For example ExtJs implements the following subclasses:</p>
39357  * <pre><code>
39358 Provider
39359 |
39360 +---{@link Ext.direct.JsonProvider JsonProvider} 
39361     |
39362     +---{@link Ext.direct.PollingProvider PollingProvider}   
39363     |
39364     +---{@link Ext.direct.RemotingProvider RemotingProvider}   
39365  * </code></pre>
39366  * @abstract
39367  */
39368 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    
39369     /**
39370      * @cfg {String} id
39371      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).
39372      * You should assign an id if you need to be able to access the provider later and you do
39373      * not have an object reference available, for example:
39374      * <pre><code>
39375 Ext.Direct.addProvider(
39376     {
39377         type: 'polling',
39378         url:  'php/poll.php',
39379         id:   'poll-provider'
39380     }
39381 );
39382      
39383 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');
39384 p.disconnect();
39385      * </code></pre>
39386      */
39387         
39388     /**
39389      * @cfg {Number} priority
39390      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).
39391      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.
39392      */    
39393     priority: 1,
39394
39395     /**
39396      * @cfg {String} type
39397      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified
39398      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a
39399      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>
39400      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>
39401      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>
39402      * </ul></div>
39403      */    
39404  
39405     // private
39406     constructor : function(config){
39407         Ext.apply(this, config);
39408         this.addEvents(
39409             /**
39410              * @event connect
39411              * Fires when the Provider connects to the server-side
39412              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39413              */            
39414             'connect',
39415             /**
39416              * @event disconnect
39417              * Fires when the Provider disconnects from the server-side
39418              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39419              */            
39420             'disconnect',
39421             /**
39422              * @event data
39423              * Fires when the Provider receives data from the server-side
39424              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39425              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
39426              */            
39427             'data',
39428             /**
39429              * @event exception
39430              * Fires when the Provider receives an exception from the server-side
39431              */                        
39432             'exception'
39433         );
39434         Ext.direct.Provider.superclass.constructor.call(this, config);
39435     },
39436
39437     /**
39438      * Returns whether or not the server-side is currently connected.
39439      * Abstract method for subclasses to implement.
39440      */
39441     isConnected: function(){
39442         return false;
39443     },
39444
39445     /**
39446      * Abstract methods for subclasses to implement.
39447      */
39448     connect: Ext.emptyFn,
39449     
39450     /**
39451      * Abstract methods for subclasses to implement.
39452      */
39453     disconnect: Ext.emptyFn
39454 });
39455 /**
39456  * @class Ext.direct.JsonProvider
39457  * @extends Ext.direct.Provider
39458  */
39459 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {
39460     parseResponse: function(xhr){
39461         if(!Ext.isEmpty(xhr.responseText)){
39462             if(typeof xhr.responseText == 'object'){
39463                 return xhr.responseText;
39464             }
39465             return Ext.decode(xhr.responseText);
39466         }
39467         return null;
39468     },
39469
39470     getEvents: function(xhr){
39471         var data = null;
39472         try{
39473             data = this.parseResponse(xhr);
39474         }catch(e){
39475             var event = new Ext.Direct.ExceptionEvent({
39476                 data: e,
39477                 xhr: xhr,
39478                 code: Ext.Direct.exceptions.PARSE,
39479                 message: 'Error parsing json response: \n\n ' + data
39480             });
39481             return [event];
39482         }
39483         var events = [];
39484         if(Ext.isArray(data)){
39485             for(var i = 0, len = data.length; i < len; i++){
39486                 events.push(Ext.Direct.createEvent(data[i]));
39487             }
39488         }else{
39489             events.push(Ext.Direct.createEvent(data));
39490         }
39491         return events;
39492     }
39493 });/**
39494  * @class Ext.direct.PollingProvider
39495  * @extends Ext.direct.JsonProvider
39496  *
39497  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.
39498  * The initial request for data originates from the client, and then is responded to by the
39499  * server.</p>
39500  * 
39501  * <p>All configurations for the PollingProvider should be generated by the server-side
39502  * API portion of the Ext.Direct stack.</p>
39503  *
39504  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply
39505  * specifying <tt>type = 'polling'</tt>.  For example:</p>
39506  * <pre><code>
39507 var pollA = new Ext.direct.PollingProvider({
39508     type:'polling',
39509     url: 'php/pollA.php',
39510 });
39511 Ext.Direct.addProvider(pollA);
39512 pollA.disconnect();
39513
39514 Ext.Direct.addProvider(
39515     {
39516         type:'polling',
39517         url: 'php/pollB.php',
39518         id: 'pollB-provider'
39519     }
39520 );
39521 var pollB = Ext.Direct.getProvider('pollB-provider');
39522  * </code></pre>
39523  */
39524 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {
39525     /**
39526      * @cfg {Number} priority
39527      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.
39528      */
39529     // override default priority
39530     priority: 3,
39531     
39532     /**
39533      * @cfg {Number} interval
39534      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every
39535      * 3 seconds).
39536      */
39537     interval: 3000,
39538
39539     /**
39540      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
39541      * on every polling request
39542      */
39543     
39544     /**
39545      * @cfg {String/Function} url
39546      * The url which the PollingProvider should contact with each request. This can also be
39547      * an imported Ext.Direct method which will accept the baseParams as its only argument.
39548      */
39549
39550     // private
39551     constructor : function(config){
39552         Ext.direct.PollingProvider.superclass.constructor.call(this, config);
39553         this.addEvents(
39554             /**
39555              * @event beforepoll
39556              * Fired immediately before a poll takes place, an event handler can return false
39557              * in order to cancel the poll.
39558              * @param {Ext.direct.PollingProvider}
39559              */
39560             'beforepoll',            
39561             /**
39562              * @event poll
39563              * This event has not yet been implemented.
39564              * @param {Ext.direct.PollingProvider}
39565              */
39566             'poll'
39567         );
39568     },
39569
39570     // inherited
39571     isConnected: function(){
39572         return !!this.pollTask;
39573     },
39574
39575     /**
39576      * Connect to the server-side and begin the polling process. To handle each
39577      * response subscribe to the data event.
39578      */
39579     connect: function(){
39580         if(this.url && !this.pollTask){
39581             this.pollTask = Ext.TaskMgr.start({
39582                 run: function(){
39583                     if(this.fireEvent('beforepoll', this) !== false){
39584                         if(typeof this.url == 'function'){
39585                             this.url(this.baseParams);
39586                         }else{
39587                             Ext.Ajax.request({
39588                                 url: this.url,
39589                                 callback: this.onData,
39590                                 scope: this,
39591                                 params: this.baseParams
39592                             });
39593                         }
39594                     }
39595                 },
39596                 interval: this.interval,
39597                 scope: this
39598             });
39599             this.fireEvent('connect', this);
39600         }else if(!this.url){
39601             throw 'Error initializing PollingProvider, no url configured.';
39602         }
39603     },
39604
39605     /**
39606      * Disconnect from the server-side and stop the polling process. The disconnect
39607      * event will be fired on a successful disconnect.
39608      */
39609     disconnect: function(){
39610         if(this.pollTask){
39611             Ext.TaskMgr.stop(this.pollTask);
39612             delete this.pollTask;
39613             this.fireEvent('disconnect', this);
39614         }
39615     },
39616
39617     // private
39618     onData: function(opt, success, xhr){
39619         if(success){
39620             var events = this.getEvents(xhr);
39621             for(var i = 0, len = events.length; i < len; i++){
39622                 var e = events[i];
39623                 this.fireEvent('data', this, e);
39624             }
39625         }else{
39626             var e = new Ext.Direct.ExceptionEvent({
39627                 data: e,
39628                 code: Ext.Direct.exceptions.TRANSPORT,
39629                 message: 'Unable to connect to the server.',
39630                 xhr: xhr
39631             });
39632             this.fireEvent('data', this, e);
39633         }
39634     }
39635 });
39636
39637 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**
39638  * @class Ext.direct.RemotingProvider
39639  * @extends Ext.direct.JsonProvider
39640  * 
39641  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
39642  * server side methods on the client (a remote procedure call (RPC) type of
39643  * connection where the client can initiate a procedure on the server).</p>
39644  * 
39645  * <p>This allows for code to be organized in a fashion that is maintainable,
39646  * while providing a clear path between client and server, something that is
39647  * not always apparent when using URLs.</p>
39648  * 
39649  * <p>To accomplish this the server-side needs to describe what classes and methods
39650  * are available on the client-side. This configuration will typically be
39651  * outputted by the server-side Ext.Direct stack when the API description is built.</p>
39652  */
39653 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       
39654     /**
39655      * @cfg {Object} actions
39656      * Object literal defining the server side actions and methods. For example, if
39657      * the Provider is configured with:
39658      * <pre><code>
39659 "actions":{ // each property within the 'actions' object represents a server side Class 
39660     "TestAction":[ // array of methods within each server side Class to be   
39661     {              // stubbed out on client
39662         "name":"doEcho", 
39663         "len":1            
39664     },{
39665         "name":"multiply",// name of method
39666         "len":2           // The number of parameters that will be used to create an
39667                           // array of data to send to the server side function.
39668                           // Ensure the server sends back a Number, not a String. 
39669     },{
39670         "name":"doForm",
39671         "formHandler":true, // direct the client to use specialized form handling method 
39672         "len":1
39673     }]
39674 }
39675      * </code></pre>
39676      * <p>Note that a Store is not required, a server method can be called at any time.
39677      * In the following example a <b>client side</b> handler is used to call the
39678      * server side method "multiply" in the server-side "TestAction" Class:</p>
39679      * <pre><code>
39680 TestAction.multiply(
39681     2, 4, // pass two arguments to server, so specify len=2
39682     // callback function after the server is called
39683     // result: the result returned by the server
39684     //      e: Ext.Direct.RemotingEvent object
39685     function(result, e){
39686         var t = e.getTransaction();
39687         var action = t.action; // server side Class called
39688         var method = t.method; // server side method called
39689         if(e.status){
39690             var answer = Ext.encode(result); // 8
39691     
39692         }else{
39693             var msg = e.message; // failure message
39694         }
39695     }
39696 );
39697      * </code></pre>
39698      * In the example above, the server side "multiply" function will be passed two
39699      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be
39700      * available as the <tt>result</tt> in the example above. 
39701      */
39702     
39703     /**
39704      * @cfg {String/Object} namespace
39705      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
39706      * Explicitly specify the namespace Object, or specify a String to have a
39707      * {@link Ext#namespace namespace created} implicitly.
39708      */
39709     
39710     /**
39711      * @cfg {String} url
39712      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. 
39713      */
39714     
39715     /**
39716      * @cfg {String} enableUrlEncode
39717      * Specify which param will hold the arguments for the method.
39718      * Defaults to <tt>'data'</tt>.
39719      */
39720     
39721     /**
39722      * @cfg {Number/Boolean} enableBuffer
39723      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
39724      * calls. If a number is specified this is the amount of time in milliseconds
39725      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>
39726      * <br><p>Calls which are received within the specified timeframe will be
39727      * concatenated together and sent in a single request, optimizing the
39728      * application by reducing the amount of round trips that have to be made
39729      * to the server.</p>
39730      */
39731     enableBuffer: 10,
39732     
39733     /**
39734      * @cfg {Number} maxRetries
39735      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.
39736      */
39737     maxRetries: 1,
39738     
39739     /**
39740      * @cfg {Number} timeout
39741      * The timeout to use for each request. Defaults to <tt>undefined</tt>.
39742      */
39743     timeout: undefined,
39744
39745     constructor : function(config){
39746         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);
39747         this.addEvents(
39748             /**
39749              * @event beforecall
39750              * Fires immediately before the client-side sends off the RPC call.
39751              * By returning false from an event handler you can prevent the call from
39752              * executing.
39753              * @param {Ext.direct.RemotingProvider} provider
39754              * @param {Ext.Direct.Transaction} transaction
39755              */            
39756             'beforecall',            
39757             /**
39758              * @event call
39759              * Fires immediately after the request to the server-side is sent. This does
39760              * NOT fire after the response has come back from the call.
39761              * @param {Ext.direct.RemotingProvider} provider
39762              * @param {Ext.Direct.Transaction} transaction
39763              */            
39764             'call'
39765         );
39766         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;
39767         this.transactions = {};
39768         this.callBuffer = [];
39769     },
39770
39771     // private
39772     initAPI : function(){
39773         var o = this.actions;
39774         for(var c in o){
39775             var cls = this.namespace[c] || (this.namespace[c] = {}),
39776                 ms = o[c];
39777             for(var i = 0, len = ms.length; i < len; i++){
39778                 var m = ms[i];
39779                 cls[m.name] = this.createMethod(c, m);
39780             }
39781         }
39782     },
39783
39784     // inherited
39785     isConnected: function(){
39786         return !!this.connected;
39787     },
39788
39789     connect: function(){
39790         if(this.url){
39791             this.initAPI();
39792             this.connected = true;
39793             this.fireEvent('connect', this);
39794         }else if(!this.url){
39795             throw 'Error initializing RemotingProvider, no url configured.';
39796         }
39797     },
39798
39799     disconnect: function(){
39800         if(this.connected){
39801             this.connected = false;
39802             this.fireEvent('disconnect', this);
39803         }
39804     },
39805
39806     onData: function(opt, success, xhr){
39807         if(success){
39808             var events = this.getEvents(xhr);
39809             for(var i = 0, len = events.length; i < len; i++){
39810                 var e = events[i],
39811                     t = this.getTransaction(e);
39812                 this.fireEvent('data', this, e);
39813                 if(t){
39814                     this.doCallback(t, e, true);
39815                     Ext.Direct.removeTransaction(t);
39816                 }
39817             }
39818         }else{
39819             var ts = [].concat(opt.ts);
39820             for(var i = 0, len = ts.length; i < len; i++){
39821                 var t = this.getTransaction(ts[i]);
39822                 if(t && t.retryCount < this.maxRetries){
39823                     t.retry();
39824                 }else{
39825                     var e = new Ext.Direct.ExceptionEvent({
39826                         data: e,
39827                         transaction: t,
39828                         code: Ext.Direct.exceptions.TRANSPORT,
39829                         message: 'Unable to connect to the server.',
39830                         xhr: xhr
39831                     });
39832                     this.fireEvent('data', this, e);
39833                     if(t){
39834                         this.doCallback(t, e, false);
39835                         Ext.Direct.removeTransaction(t);
39836                     }
39837                 }
39838             }
39839         }
39840     },
39841
39842     getCallData: function(t){
39843         return {
39844             action: t.action,
39845             method: t.method,
39846             data: t.data,
39847             type: 'rpc',
39848             tid: t.tid
39849         };
39850     },
39851
39852     doSend : function(data){
39853         var o = {
39854             url: this.url,
39855             callback: this.onData,
39856             scope: this,
39857             ts: data,
39858             timeout: this.timeout
39859         }, callData;
39860
39861         if(Ext.isArray(data)){
39862             callData = [];
39863             for(var i = 0, len = data.length; i < len; i++){
39864                 callData.push(this.getCallData(data[i]));
39865             }
39866         }else{
39867             callData = this.getCallData(data);
39868         }
39869
39870         if(this.enableUrlEncode){
39871             var params = {};
39872             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);
39873             o.params = params;
39874         }else{
39875             o.jsonData = callData;
39876         }
39877         Ext.Ajax.request(o);
39878     },
39879
39880     combineAndSend : function(){
39881         var len = this.callBuffer.length;
39882         if(len > 0){
39883             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);
39884             this.callBuffer = [];
39885         }
39886     },
39887
39888     queueTransaction: function(t){
39889         if(t.form){
39890             this.processForm(t);
39891             return;
39892         }
39893         this.callBuffer.push(t);
39894         if(this.enableBuffer){
39895             if(!this.callTask){
39896                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);
39897             }
39898             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);
39899         }else{
39900             this.combineAndSend();
39901         }
39902     },
39903
39904     doCall : function(c, m, args){
39905         var data = null, hs = args[m.len], scope = args[m.len+1];
39906
39907         if(m.len !== 0){
39908             data = args.slice(0, m.len);
39909         }
39910
39911         var t = new Ext.Direct.Transaction({
39912             provider: this,
39913             args: args,
39914             action: c,
39915             method: m.name,
39916             data: data,
39917             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs
39918         });
39919
39920         if(this.fireEvent('beforecall', this, t) !== false){
39921             Ext.Direct.addTransaction(t);
39922             this.queueTransaction(t);
39923             this.fireEvent('call', this, t);
39924         }
39925     },
39926
39927     doForm : function(c, m, form, callback, scope){
39928         var t = new Ext.Direct.Transaction({
39929             provider: this,
39930             action: c,
39931             method: m.name,
39932             args:[form, callback, scope],
39933             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,
39934             isForm: true
39935         });
39936
39937         if(this.fireEvent('beforecall', this, t) !== false){
39938             Ext.Direct.addTransaction(t);
39939             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',
39940                 params = {
39941                     extTID: t.tid,
39942                     extAction: c,
39943                     extMethod: m.name,
39944                     extType: 'rpc',
39945                     extUpload: String(isUpload)
39946                 };
39947             
39948             // change made from typeof callback check to callback.params
39949             // to support addl param passing in DirectSubmit EAC 6/2
39950             Ext.apply(t, {
39951                 form: Ext.getDom(form),
39952                 isUpload: isUpload,
39953                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
39954             });
39955             this.fireEvent('call', this, t);
39956             this.processForm(t);
39957         }
39958     },
39959     
39960     processForm: function(t){
39961         Ext.Ajax.request({
39962             url: this.url,
39963             params: t.params,
39964             callback: this.onData,
39965             scope: this,
39966             form: t.form,
39967             isUpload: t.isUpload,
39968             ts: t
39969         });
39970     },
39971
39972     createMethod : function(c, m){
39973         var f;
39974         if(!m.formHandler){
39975             f = function(){
39976                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));
39977             }.createDelegate(this);
39978         }else{
39979             f = function(form, callback, scope){
39980                 this.doForm(c, m, form, callback, scope);
39981             }.createDelegate(this);
39982         }
39983         f.directCfg = {
39984             action: c,
39985             method: m
39986         };
39987         return f;
39988     },
39989
39990     getTransaction: function(opt){
39991         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;
39992     },
39993
39994     doCallback: function(t, e){
39995         var fn = e.status ? 'success' : 'failure';
39996         if(t && t.cb){
39997             var hs = t.cb,
39998                 result = Ext.isDefined(e.result) ? e.result : e.data;
39999             if(Ext.isFunction(hs)){
40000                 hs(result, e);
40001             } else{
40002                 Ext.callback(hs[fn], hs.scope, [result, e]);
40003                 Ext.callback(hs.callback, hs.scope, [result, e]);
40004             }
40005         }
40006     }
40007 });
40008 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;/**
40009  * @class Ext.Resizable
40010  * @extends Ext.util.Observable
40011  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
40012  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
40013  * the textarea in a div and set 'resizeChild' to true (or to the id of the element), <b>or</b> set wrap:true in your config and
40014  * the element will be wrapped for you automatically.</p>
40015  * <p>Here is the list of valid resize handles:</p>
40016  * <pre>
40017 Value   Description
40018 ------  -------------------
40019  'n'     north
40020  's'     south
40021  'e'     east
40022  'w'     west
40023  'nw'    northwest
40024  'sw'    southwest
40025  'se'    southeast
40026  'ne'    northeast
40027  'all'   all
40028 </pre>
40029  * <p>Here's an example showing the creation of a typical Resizable:</p>
40030  * <pre><code>
40031 var resizer = new Ext.Resizable('element-id', {
40032     handles: 'all',
40033     minWidth: 200,
40034     minHeight: 100,
40035     maxWidth: 500,
40036     maxHeight: 400,
40037     pinned: true
40038 });
40039 resizer.on('resize', myHandler);
40040 </code></pre>
40041  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
40042  * resizer.east.setDisplayed(false);</p>
40043  * @constructor
40044  * Create a new resizable component
40045  * @param {Mixed} el The id or element to resize
40046  * @param {Object} config configuration options
40047   */
40048 Ext.Resizable = Ext.extend(Ext.util.Observable, {
40049
40050     constructor: function(el, config){
40051         this.el = Ext.get(el);
40052         if(config && config.wrap){
40053             config.resizeChild = this.el;
40054             this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : {cls:'xresizable-wrap'});
40055             this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';
40056             this.el.setStyle('overflow', 'hidden');
40057             this.el.setPositioning(config.resizeChild.getPositioning());
40058             config.resizeChild.clearPositioning();
40059             if(!config.width || !config.height){
40060                 var csize = config.resizeChild.getSize();
40061                 this.el.setSize(csize.width, csize.height);
40062             }
40063             if(config.pinned && !config.adjustments){
40064                 config.adjustments = 'auto';
40065             }
40066         }
40067
40068         /**
40069          * The proxy Element that is resized in place of the real Element during the resize operation.
40070          * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.
40071          * Read only.
40072          * @type Ext.Element.
40073          * @property proxy
40074          */
40075         this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());
40076         this.proxy.unselectable();
40077         this.proxy.enableDisplayMode('block');
40078
40079         Ext.apply(this, config);
40080
40081         if(this.pinned){
40082             this.disableTrackOver = true;
40083             this.el.addClass('x-resizable-pinned');
40084         }
40085         // if the element isn't positioned, make it relative
40086         var position = this.el.getStyle('position');
40087         if(position != 'absolute' && position != 'fixed'){
40088             this.el.setStyle('position', 'relative');
40089         }
40090         if(!this.handles){ // no handles passed, must be legacy style
40091             this.handles = 's,e,se';
40092             if(this.multiDirectional){
40093                 this.handles += ',n,w';
40094             }
40095         }
40096         if(this.handles == 'all'){
40097             this.handles = 'n s e w ne nw se sw';
40098         }
40099         var hs = this.handles.split(/\s*?[,;]\s*?| /);
40100         var ps = Ext.Resizable.positions;
40101         for(var i = 0, len = hs.length; i < len; i++){
40102             if(hs[i] && ps[hs[i]]){
40103                 var pos = ps[hs[i]];
40104                 this[pos] = new Ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent, this.handleCls);
40105             }
40106         }
40107         // legacy
40108         this.corner = this.southeast;
40109
40110         if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){
40111             this.updateBox = true;
40112         }
40113
40114         this.activeHandle = null;
40115
40116         if(this.resizeChild){
40117             if(typeof this.resizeChild == 'boolean'){
40118                 this.resizeChild = Ext.get(this.el.dom.firstChild, true);
40119             }else{
40120                 this.resizeChild = Ext.get(this.resizeChild, true);
40121             }
40122         }
40123
40124         if(this.adjustments == 'auto'){
40125             var rc = this.resizeChild;
40126             var hw = this.west, he = this.east, hn = this.north, hs = this.south;
40127             if(rc && (hw || hn)){
40128                 rc.position('relative');
40129                 rc.setLeft(hw ? hw.el.getWidth() : 0);
40130                 rc.setTop(hn ? hn.el.getHeight() : 0);
40131             }
40132             this.adjustments = [
40133                 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
40134                 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
40135             ];
40136         }
40137
40138         if(this.draggable){
40139             this.dd = this.dynamic ?
40140                 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
40141             this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
40142             if(this.constrainTo){
40143                 this.dd.constrainTo(this.constrainTo);
40144             }
40145         }
40146
40147         this.addEvents(
40148             /**
40149              * @event beforeresize
40150              * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.
40151              * @param {Ext.Resizable} this
40152              * @param {Ext.EventObject} e The mousedown event
40153              */
40154             'beforeresize',
40155             /**
40156              * @event resize
40157              * Fired after a resize.
40158              * @param {Ext.Resizable} this
40159              * @param {Number} width The new width
40160              * @param {Number} height The new height
40161              * @param {Ext.EventObject} e The mouseup event
40162              */
40163             'resize'
40164         );
40165
40166         if(this.width !== null && this.height !== null){
40167             this.resizeTo(this.width, this.height);
40168         }else{
40169             this.updateChildSize();
40170         }
40171         if(Ext.isIE){
40172             this.el.dom.style.zoom = 1;
40173         }
40174         Ext.Resizable.superclass.constructor.call(this);
40175     },
40176
40177     /**
40178      * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the
40179      * resize operation's new size (defaults to <tt>[0, 0]</tt>)
40180      */
40181     adjustments : [0, 0],
40182     /**
40183      * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
40184      */
40185     animate : false,
40186     /**
40187      * @cfg {Mixed} constrainTo Constrain the resize to a particular element
40188      */
40189     /**
40190      * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
40191      */
40192     disableTrackOver : false,
40193     /**
40194      * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
40195      */
40196     draggable: false,
40197     /**
40198      * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)
40199      */
40200     duration : 0.35,
40201     /**
40202      * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
40203      */
40204     dynamic : false,
40205     /**
40206      * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)
40207      */
40208     easing : 'easeOutStrong',
40209     /**
40210      * @cfg {Boolean} enabled False to disable resizing (defaults to true)
40211      */
40212     enabled : true,
40213     /**
40214      * @property enabled Writable. False if resizing is disabled.
40215      * @type Boolean
40216      */
40217     /**
40218      * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).
40219      * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.
40220      */
40221     handles : false,
40222     /**
40223      * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.
40224      */
40225     multiDirectional : false,
40226     /**
40227      * @cfg {Number} height The height of the element in pixels (defaults to null)
40228      */
40229     height : null,
40230     /**
40231      * @cfg {Number} width The width of the element in pixels (defaults to null)
40232      */
40233     width : null,
40234     /**
40235      * @cfg {Number} heightIncrement The increment to snap the height resize in pixels
40236      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.
40237      */
40238     heightIncrement : 0,
40239     /**
40240      * @cfg {Number} widthIncrement The increment to snap the width resize in pixels
40241      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.
40242      */
40243     widthIncrement : 0,
40244     /**
40245      * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
40246      */
40247     minHeight : 5,
40248     /**
40249      * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
40250      */
40251     minWidth : 5,
40252     /**
40253      * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
40254      */
40255     maxHeight : 10000,
40256     /**
40257      * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
40258      */
40259     maxWidth : 10000,
40260     /**
40261      * @cfg {Number} minX The minimum x for the element (defaults to 0)
40262      */
40263     minX: 0,
40264     /**
40265      * @cfg {Number} minY The minimum x for the element (defaults to 0)
40266      */
40267     minY: 0,
40268     /**
40269      * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
40270      * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
40271      */
40272     pinned : false,
40273     /**
40274      * @cfg {Boolean} preserveRatio True to preserve the original ratio between height
40275      * and width during resize (defaults to false)
40276      */
40277     preserveRatio : false,
40278     /**
40279      * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
40280      */
40281     resizeChild : false,
40282     /**
40283      * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
40284      */
40285     transparent: false,
40286     /**
40287      * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region
40288      */
40289     /**
40290      * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
40291      * in favor of the handles config option (defaults to false)
40292      */
40293     /**
40294      * @cfg {String} handleCls A css class to add to each handle. Defaults to <tt>''</tt>.
40295      */
40296
40297
40298     /**
40299      * Perform a manual resize and fires the 'resize' event.
40300      * @param {Number} width
40301      * @param {Number} height
40302      */
40303     resizeTo : function(width, height){
40304         this.el.setSize(width, height);
40305         this.updateChildSize();
40306         this.fireEvent('resize', this, width, height, null);
40307     },
40308
40309     // private
40310     startSizing : function(e, handle){
40311         this.fireEvent('beforeresize', this, e);
40312         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
40313
40314             if(!this.overlay){
40315                 this.overlay = this.el.createProxy({tag: 'div', cls: 'x-resizable-overlay', html: '&#160;'}, Ext.getBody());
40316                 this.overlay.unselectable();
40317                 this.overlay.enableDisplayMode('block');
40318                 this.overlay.on({
40319                     scope: this,
40320                     mousemove: this.onMouseMove,
40321                     mouseup: this.onMouseUp
40322                 });
40323             }
40324             this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));
40325
40326             this.resizing = true;
40327             this.startBox = this.el.getBox();
40328             this.startPoint = e.getXY();
40329             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
40330                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
40331
40332             this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
40333             this.overlay.show();
40334
40335             if(this.constrainTo) {
40336                 var ct = Ext.get(this.constrainTo);
40337                 this.resizeRegion = ct.getRegion().adjust(
40338                     ct.getFrameWidth('t'),
40339                     ct.getFrameWidth('l'),
40340                     -ct.getFrameWidth('b'),
40341                     -ct.getFrameWidth('r')
40342                 );
40343             }
40344
40345             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
40346             this.proxy.show();
40347             this.proxy.setBox(this.startBox);
40348             if(!this.dynamic){
40349                 this.proxy.setStyle('visibility', 'visible');
40350             }
40351         }
40352     },
40353
40354     // private
40355     onMouseDown : function(handle, e){
40356         if(this.enabled){
40357             e.stopEvent();
40358             this.activeHandle = handle;
40359             this.startSizing(e, handle);
40360         }
40361     },
40362
40363     // private
40364     onMouseUp : function(e){
40365         this.activeHandle = null;
40366         var size = this.resizeElement();
40367         this.resizing = false;
40368         this.handleOut();
40369         this.overlay.hide();
40370         this.proxy.hide();
40371         this.fireEvent('resize', this, size.width, size.height, e);
40372     },
40373
40374     // private
40375     updateChildSize : function(){
40376         if(this.resizeChild){
40377             var el = this.el;
40378             var child = this.resizeChild;
40379             var adj = this.adjustments;
40380             if(el.dom.offsetWidth){
40381                 var b = el.getSize(true);
40382                 child.setSize(b.width+adj[0], b.height+adj[1]);
40383             }
40384             // Second call here for IE
40385             // The first call enables instant resizing and
40386             // the second call corrects scroll bars if they
40387             // exist
40388             if(Ext.isIE){
40389                 setTimeout(function(){
40390                     if(el.dom.offsetWidth){
40391                         var b = el.getSize(true);
40392                         child.setSize(b.width+adj[0], b.height+adj[1]);
40393                     }
40394                 }, 10);
40395             }
40396         }
40397     },
40398
40399     // private
40400     snap : function(value, inc, min){
40401         if(!inc || !value){
40402             return value;
40403         }
40404         var newValue = value;
40405         var m = value % inc;
40406         if(m > 0){
40407             if(m > (inc/2)){
40408                 newValue = value + (inc-m);
40409             }else{
40410                 newValue = value - m;
40411             }
40412         }
40413         return Math.max(min, newValue);
40414     },
40415
40416     /**
40417      * <p>Performs resizing of the associated Element. This method is called internally by this
40418      * class, and should not be called by user code.</p>
40419      * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI
40420      * component such as a Panel, this method may be overridden by specifying an implementation
40421      * as a config option to provide appropriate behaviour at the end of the resize operation on
40422      * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>
40423      * <p>The new area to be resized to is available by examining the state of the {@link #proxy}
40424      * Element. Example:
40425 <pre><code>
40426 new Ext.Panel({
40427     title: 'Resize me',
40428     x: 100,
40429     y: 100,
40430     renderTo: Ext.getBody(),
40431     floating: true,
40432     frame: true,
40433     width: 400,
40434     height: 200,
40435     listeners: {
40436         render: function(p) {
40437             new Ext.Resizable(p.getEl(), {
40438                 handles: 'all',
40439                 pinned: true,
40440                 transparent: true,
40441                 resizeElement: function() {
40442                     var box = this.proxy.getBox();
40443                     p.updateBox(box);
40444                     if (p.layout) {
40445                         p.doLayout();
40446                     }
40447                     return box;
40448                 }
40449            });
40450        }
40451     }
40452 }).show();
40453 </code></pre>
40454      */
40455     resizeElement : function(){
40456         var box = this.proxy.getBox();
40457         if(this.updateBox){
40458             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
40459         }else{
40460             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
40461         }
40462         this.updateChildSize();
40463         if(!this.dynamic){
40464             this.proxy.hide();
40465         }
40466         if(this.draggable && this.constrainTo){
40467             this.dd.resetConstraints();
40468             this.dd.constrainTo(this.constrainTo);
40469         }
40470         return box;
40471     },
40472
40473     // private
40474     constrain : function(v, diff, m, mx){
40475         if(v - diff < m){
40476             diff = v - m;
40477         }else if(v - diff > mx){
40478             diff = v - mx;
40479         }
40480         return diff;
40481     },
40482
40483     // private
40484     onMouseMove : function(e){
40485         if(this.enabled && this.activeHandle){
40486             try{// try catch so if something goes wrong the user doesn't get hung
40487
40488             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
40489                 return;
40490             }
40491
40492             //var curXY = this.startPoint;
40493             var curSize = this.curSize || this.startBox,
40494                 x = this.startBox.x, y = this.startBox.y,
40495                 ox = x,
40496                 oy = y,
40497                 w = curSize.width,
40498                 h = curSize.height,
40499                 ow = w,
40500                 oh = h,
40501                 mw = this.minWidth,
40502                 mh = this.minHeight,
40503                 mxw = this.maxWidth,
40504                 mxh = this.maxHeight,
40505                 wi = this.widthIncrement,
40506                 hi = this.heightIncrement,
40507                 eventXY = e.getXY(),
40508                 diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),
40509                 diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),
40510                 pos = this.activeHandle.position,
40511                 tw,
40512                 th;
40513
40514             switch(pos){
40515                 case 'east':
40516                     w += diffX;
40517                     w = Math.min(Math.max(mw, w), mxw);
40518                     break;
40519                 case 'south':
40520                     h += diffY;
40521                     h = Math.min(Math.max(mh, h), mxh);
40522                     break;
40523                 case 'southeast':
40524                     w += diffX;
40525                     h += diffY;
40526                     w = Math.min(Math.max(mw, w), mxw);
40527                     h = Math.min(Math.max(mh, h), mxh);
40528                     break;
40529                 case 'north':
40530                     diffY = this.constrain(h, diffY, mh, mxh);
40531                     y += diffY;
40532                     h -= diffY;
40533                     break;
40534                 case 'west':
40535                     diffX = this.constrain(w, diffX, mw, mxw);
40536                     x += diffX;
40537                     w -= diffX;
40538                     break;
40539                 case 'northeast':
40540                     w += diffX;
40541                     w = Math.min(Math.max(mw, w), mxw);
40542                     diffY = this.constrain(h, diffY, mh, mxh);
40543                     y += diffY;
40544                     h -= diffY;
40545                     break;
40546                 case 'northwest':
40547                     diffX = this.constrain(w, diffX, mw, mxw);
40548                     diffY = this.constrain(h, diffY, mh, mxh);
40549                     y += diffY;
40550                     h -= diffY;
40551                     x += diffX;
40552                     w -= diffX;
40553                     break;
40554                case 'southwest':
40555                     diffX = this.constrain(w, diffX, mw, mxw);
40556                     h += diffY;
40557                     h = Math.min(Math.max(mh, h), mxh);
40558                     x += diffX;
40559                     w -= diffX;
40560                     break;
40561             }
40562
40563             var sw = this.snap(w, wi, mw);
40564             var sh = this.snap(h, hi, mh);
40565             if(sw != w || sh != h){
40566                 switch(pos){
40567                     case 'northeast':
40568                         y -= sh - h;
40569                     break;
40570                     case 'north':
40571                         y -= sh - h;
40572                         break;
40573                     case 'southwest':
40574                         x -= sw - w;
40575                     break;
40576                     case 'west':
40577                         x -= sw - w;
40578                         break;
40579                     case 'northwest':
40580                         x -= sw - w;
40581                         y -= sh - h;
40582                     break;
40583                 }
40584                 w = sw;
40585                 h = sh;
40586             }
40587
40588             if(this.preserveRatio){
40589                 switch(pos){
40590                     case 'southeast':
40591                     case 'east':
40592                         h = oh * (w/ow);
40593                         h = Math.min(Math.max(mh, h), mxh);
40594                         w = ow * (h/oh);
40595                        break;
40596                     case 'south':
40597                         w = ow * (h/oh);
40598                         w = Math.min(Math.max(mw, w), mxw);
40599                         h = oh * (w/ow);
40600                         break;
40601                     case 'northeast':
40602                         w = ow * (h/oh);
40603                         w = Math.min(Math.max(mw, w), mxw);
40604                         h = oh * (w/ow);
40605                     break;
40606                     case 'north':
40607                         tw = w;
40608                         w = ow * (h/oh);
40609                         w = Math.min(Math.max(mw, w), mxw);
40610                         h = oh * (w/ow);
40611                         x += (tw - w) / 2;
40612                         break;
40613                     case 'southwest':
40614                         h = oh * (w/ow);
40615                         h = Math.min(Math.max(mh, h), mxh);
40616                         tw = w;
40617                         w = ow * (h/oh);
40618                         x += tw - w;
40619                         break;
40620                     case 'west':
40621                         th = h;
40622                         h = oh * (w/ow);
40623                         h = Math.min(Math.max(mh, h), mxh);
40624                         y += (th - h) / 2;
40625                         tw = w;
40626                         w = ow * (h/oh);
40627                         x += tw - w;
40628                        break;
40629                     case 'northwest':
40630                         tw = w;
40631                         th = h;
40632                         h = oh * (w/ow);
40633                         h = Math.min(Math.max(mh, h), mxh);
40634                         w = ow * (h/oh);
40635                         y += th - h;
40636                         x += tw - w;
40637                         break;
40638
40639                 }
40640             }
40641             this.proxy.setBounds(x, y, w, h);
40642             if(this.dynamic){
40643                 this.resizeElement();
40644             }
40645             }catch(ex){}
40646         }
40647     },
40648
40649     // private
40650     handleOver : function(){
40651         if(this.enabled){
40652             this.el.addClass('x-resizable-over');
40653         }
40654     },
40655
40656     // private
40657     handleOut : function(){
40658         if(!this.resizing){
40659             this.el.removeClass('x-resizable-over');
40660         }
40661     },
40662
40663     /**
40664      * Returns the element this component is bound to.
40665      * @return {Ext.Element}
40666      */
40667     getEl : function(){
40668         return this.el;
40669     },
40670
40671     /**
40672      * Returns the resizeChild element (or null).
40673      * @return {Ext.Element}
40674      */
40675     getResizeChild : function(){
40676         return this.resizeChild;
40677     },
40678
40679     /**
40680      * Destroys this resizable. If the element was wrapped and
40681      * removeEl is not true then the element remains.
40682      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
40683      */
40684     destroy : function(removeEl){
40685         Ext.destroy(this.dd, this.overlay, this.proxy);
40686         this.overlay = null;
40687         this.proxy = null;
40688
40689         var ps = Ext.Resizable.positions;
40690         for(var k in ps){
40691             if(typeof ps[k] != 'function' && this[ps[k]]){
40692                 this[ps[k]].destroy();
40693             }
40694         }
40695         if(removeEl){
40696             this.el.update('');
40697             Ext.destroy(this.el);
40698             this.el = null;
40699         }
40700         this.purgeListeners();
40701     },
40702
40703     syncHandleHeight : function(){
40704         var h = this.el.getHeight(true);
40705         if(this.west){
40706             this.west.el.setHeight(h);
40707         }
40708         if(this.east){
40709             this.east.el.setHeight(h);
40710         }
40711     }
40712 });
40713
40714 // private
40715 // hash to map config positions to true positions
40716 Ext.Resizable.positions = {
40717     n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'
40718 };
40719
40720 Ext.Resizable.Handle = Ext.extend(Object, {
40721     constructor : function(rz, pos, disableTrackOver, transparent, cls){
40722        if(!this.tpl){
40723             // only initialize the template if resizable is used
40724             var tpl = Ext.DomHelper.createTemplate(
40725                 {tag: 'div', cls: 'x-resizable-handle x-resizable-handle-{0}'}
40726             );
40727             tpl.compile();
40728             Ext.Resizable.Handle.prototype.tpl = tpl;
40729         }
40730         this.position = pos;
40731         this.rz = rz;
40732         this.el = this.tpl.append(rz.el.dom, [this.position], true);
40733         this.el.unselectable();
40734         if(transparent){
40735             this.el.setOpacity(0);
40736         }
40737         if(!Ext.isEmpty(cls)){
40738             this.el.addClass(cls);
40739         }
40740         this.el.on('mousedown', this.onMouseDown, this);
40741         if(!disableTrackOver){
40742             this.el.on({
40743                 scope: this,
40744                 mouseover: this.onMouseOver,
40745                 mouseout: this.onMouseOut
40746             });
40747         }
40748     },
40749
40750     // private
40751     afterResize : function(rz){
40752         // do nothing
40753     },
40754     // private
40755     onMouseDown : function(e){
40756         this.rz.onMouseDown(this, e);
40757     },
40758     // private
40759     onMouseOver : function(e){
40760         this.rz.handleOver(this, e);
40761     },
40762     // private
40763     onMouseOut : function(e){
40764         this.rz.handleOut(this, e);
40765     },
40766     // private
40767     destroy : function(){
40768         Ext.destroy(this.el);
40769         this.el = null;
40770     }
40771 });
40772 /**
40773  * @class Ext.Window
40774  * @extends Ext.Panel
40775  * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
40776  * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
40777  * restored to their prior size, and can be {@link #minimize}d.</p>
40778  * <p>Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} to provide
40779  * grouping, activation, to front, to back and other application-specific behavior.</p>
40780  * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
40781  * specify {@link Ext.Component#renderTo renderTo}.</p>
40782  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Window resulting in
40783  * destruction of any child Components. This makes the Window object, and all its descendants <b>unusable</b>. To enable
40784  * re-use of a Window, use <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
40785  * @constructor
40786  * @param {Object} config The config object
40787  * @xtype window
40788  */
40789 Ext.Window = Ext.extend(Ext.Panel, {
40790     /**
40791      * @cfg {Number} x
40792      * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
40793      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
40794      */
40795     /**
40796      * @cfg {Number} y
40797      * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
40798      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
40799      */
40800     /**
40801      * @cfg {Boolean} modal
40802      * True to make the window modal and mask everything behind it when displayed, false to display it without
40803      * restricting access to other UI elements (defaults to false).
40804      */
40805     /**
40806      * @cfg {String/Element} animateTarget
40807      * Id or element from which the window should animate while opening (defaults to null with no animation).
40808      */
40809     /**
40810      * @cfg {String} resizeHandles
40811      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.
40812      */
40813     /**
40814      * @cfg {Ext.WindowGroup} manager
40815      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).
40816      */
40817     /**
40818     * @cfg {String/Number/Component} defaultButton
40819     * <p>Specifies a Component to receive focus when this Window is focussed.</p>
40820     * <p>This may be one of:</p><div class="mdetail-params"><ul>
40821     * <li>The index of a footer Button.</li>
40822     * <li>The id of a Component.</li>
40823     * <li>A Component.</li>
40824     * </ul></div>
40825     */
40826     /**
40827     * @cfg {Function} onEsc
40828     * Allows override of the built-in processing for the escape key. Default action
40829     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
40830     * To prevent the Window closing when the escape key is pressed, specify this as
40831     * Ext.emptyFn (See {@link Ext#emptyFn}).
40832     */
40833     /**
40834      * @cfg {Boolean} collapsed
40835      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
40836      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window
40837      * will always be expanded when shown.
40838      */
40839     /**
40840      * @cfg {Boolean} maximized
40841      * True to initially display the window in a maximized state. (Defaults to false).
40842      */
40843
40844     /**
40845     * @cfg {String} baseCls
40846     * The base CSS class to apply to this panel's element (defaults to 'x-window').
40847     */
40848     baseCls : 'x-window',
40849     /**
40850      * @cfg {Boolean} resizable
40851      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).
40852      */
40853     resizable : true,
40854     /**
40855      * @cfg {Boolean} draggable
40856      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
40857      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
40858      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).
40859      */
40860     draggable : true,
40861     /**
40862      * @cfg {Boolean} closable
40863      * <p>True to display the 'close' tool button and allow the user to close the window, false to
40864      * hide the button and disallow closing the window (defaults to true).</p>
40865      * <p>By default, when close is requested by either clicking the close button in the header
40866      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
40867      * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
40868      * it may not be reused.</p>
40869      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
40870      * {@link #closeAction} to 'hide'.
40871      */
40872     closable : true,
40873     /**
40874      * @cfg {String} closeAction
40875      * <p>The action to take when the close header tool is clicked:
40876      * <div class="mdetail-params"><ul>
40877      * <li><b><code>'{@link #close}'</code></b> : <b>Default</b><div class="sub-desc">
40878      * {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy}
40879      * it and all descendant Components. The window will <b>not</b> be available to be
40880      * redisplayed via the {@link #show} method.
40881      * </div></li>
40882      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
40883      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
40884      * The window will be available to be redisplayed via the {@link #show} method.
40885      * </div></li>
40886      * </ul></div>
40887      * <p><b>Note:</b> This setting does not affect the {@link #close} method
40888      * which will always {@link Ext.Component#destroy destroy} the window. To
40889      * programatically <i>hide</i> a window, call {@link #hide}.</p>
40890      */
40891     closeAction : 'close',
40892     /**
40893      * @cfg {Boolean} constrain
40894      * True to constrain the window within its containing element, false to allow it to fall outside of its
40895      * containing element. By default the window will be rendered to document.body.  To render and constrain the
40896      * window within another element specify {@link #renderTo}.
40897      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
40898      */
40899     constrain : false,
40900     /**
40901      * @cfg {Boolean} constrainHeader
40902      * True to constrain the window header within its containing element (allowing the window body to fall outside
40903      * of its containing element) or false to allow the header to fall outside its containing element (defaults to
40904      * false). Optionally the entire window can be constrained using {@link #constrain}.
40905      */
40906     constrainHeader : false,
40907     /**
40908      * @cfg {Boolean} plain
40909      * True to render the window body with a transparent background so that it will blend into the framing
40910      * elements, false to add a lighter background color to visually highlight the body element and separate it
40911      * more distinctly from the surrounding frame (defaults to false).
40912      */
40913     plain : false,
40914     /**
40915      * @cfg {Boolean} minimizable
40916      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
40917      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
40918      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
40919      * custom minimize behavior implemented for this option to be useful.
40920      */
40921     minimizable : false,
40922     /**
40923      * @cfg {Boolean} maximizable
40924      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
40925      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
40926      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
40927      * restore the window to its previous size.
40928      */
40929     maximizable : false,
40930     /**
40931      * @cfg {Number} minHeight
40932      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.
40933      */
40934     minHeight : 100,
40935     /**
40936      * @cfg {Number} minWidth
40937      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.
40938      */
40939     minWidth : 200,
40940     /**
40941      * @cfg {Boolean} expandOnShow
40942      * True to always expand the window when it is displayed, false to keep it in its current state (which may be
40943      * {@link #collapsed}) when displayed (defaults to true).
40944      */
40945     expandOnShow : true,
40946
40947     // inherited docs, same default
40948     collapsible : false,
40949
40950     /**
40951      * @cfg {Boolean} initHidden
40952      * True to hide the window until show() is explicitly called (defaults to true).
40953      * @deprecated
40954      */
40955     initHidden : undefined,
40956
40957     /**
40958      * @cfg {Boolean} hidden
40959      * Render this component hidden (default is <tt>true</tt>). If <tt>true</tt>, the
40960      * {@link #hide} method will be called internally.
40961      */
40962     hidden : true,
40963
40964     // The following configs are set to provide the basic functionality of a window.
40965     // Changing them would require additional code to handle correctly and should
40966     // usually only be done in subclasses that can provide custom behavior.  Changing them
40967     // may have unexpected or undesirable results.
40968     /** @cfg {String} elements @hide */
40969     elements : 'header,body',
40970     /** @cfg {Boolean} frame @hide */
40971     frame : true,
40972     /** @cfg {Boolean} floating @hide */
40973     floating : true,
40974
40975     // private
40976     initComponent : function(){
40977         this.initTools();
40978         Ext.Window.superclass.initComponent.call(this);
40979         this.addEvents(
40980             /**
40981              * @event activate
40982              * Fires after the window has been visually activated via {@link #setActive}.
40983              * @param {Ext.Window} this
40984              */
40985             /**
40986              * @event deactivate
40987              * Fires after the window has been visually deactivated via {@link #setActive}.
40988              * @param {Ext.Window} this
40989              */
40990             /**
40991              * @event resize
40992              * Fires after the window has been resized.
40993              * @param {Ext.Window} this
40994              * @param {Number} width The window's new width
40995              * @param {Number} height The window's new height
40996              */
40997             'resize',
40998             /**
40999              * @event maximize
41000              * Fires after the window has been maximized.
41001              * @param {Ext.Window} this
41002              */
41003             'maximize',
41004             /**
41005              * @event minimize
41006              * Fires after the window has been minimized.
41007              * @param {Ext.Window} this
41008              */
41009             'minimize',
41010             /**
41011              * @event restore
41012              * Fires after the window has been restored to its original size after being maximized.
41013              * @param {Ext.Window} this
41014              */
41015             'restore'
41016         );
41017         // for backwards compat, this should be removed at some point
41018         if(Ext.isDefined(this.initHidden)){
41019             this.hidden = this.initHidden;
41020         }
41021         if(this.hidden === false){
41022             this.hidden = true;
41023             this.show();
41024         }
41025     },
41026
41027     // private
41028     getState : function(){
41029         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true));
41030     },
41031
41032     // private
41033     onRender : function(ct, position){
41034         Ext.Window.superclass.onRender.call(this, ct, position);
41035
41036         if(this.plain){
41037             this.el.addClass('x-window-plain');
41038         }
41039
41040         // this element allows the Window to be focused for keyboard events
41041         this.focusEl = this.el.createChild({
41042                     tag: 'a', href:'#', cls:'x-dlg-focus',
41043                     tabIndex:'-1', html: '&#160;'});
41044         this.focusEl.swallowEvent('click', true);
41045
41046         this.proxy = this.el.createProxy('x-window-proxy');
41047         this.proxy.enableDisplayMode('block');
41048
41049         if(this.modal){
41050             this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom);
41051             this.mask.enableDisplayMode('block');
41052             this.mask.hide();
41053             this.mon(this.mask, 'click', this.focus, this);
41054         }
41055         if(this.maximizable){
41056             this.mon(this.header, 'dblclick', this.toggleMaximize, this);
41057         }
41058     },
41059
41060     // private
41061     initEvents : function(){
41062         Ext.Window.superclass.initEvents.call(this);
41063         if(this.animateTarget){
41064             this.setAnimateTarget(this.animateTarget);
41065         }
41066
41067         if(this.resizable){
41068             this.resizer = new Ext.Resizable(this.el, {
41069                 minWidth: this.minWidth,
41070                 minHeight:this.minHeight,
41071                 handles: this.resizeHandles || 'all',
41072                 pinned: true,
41073                 resizeElement : this.resizerAction,
41074                 handleCls: 'x-window-handle'
41075             });
41076             this.resizer.window = this;
41077             this.mon(this.resizer, 'beforeresize', this.beforeResize, this);
41078         }
41079
41080         if(this.draggable){
41081             this.header.addClass('x-window-draggable');
41082         }
41083         this.mon(this.el, 'mousedown', this.toFront, this);
41084         this.manager = this.manager || Ext.WindowMgr;
41085         this.manager.register(this);
41086         if(this.maximized){
41087             this.maximized = false;
41088             this.maximize();
41089         }
41090         if(this.closable){
41091             var km = this.getKeyMap();
41092             km.on(27, this.onEsc, this);
41093             km.disable();
41094         }
41095     },
41096
41097     initDraggable : function(){
41098         /**
41099          * <p>If this Window is configured {@link #draggable}, this property will contain
41100          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.</p>
41101          * <p>This has implementations of <code>startDrag</code>, <code>onDrag</code> and <code>endDrag</code>
41102          * which perform the dragging action. If extra logic is needed at these points, use
41103          * {@link Function#createInterceptor createInterceptor} or {@link Function#createSequence createSequence} to
41104          * augment the existing implementations.</p>
41105          * @type Ext.dd.DD
41106          * @property dd
41107          */
41108         this.dd = new Ext.Window.DD(this);
41109     },
41110
41111    // private
41112     onEsc : function(k, e){
41113         e.stopEvent();
41114         this[this.closeAction]();
41115     },
41116
41117     // private
41118     beforeDestroy : function(){
41119         if(this.rendered){
41120             this.hide();
41121             this.clearAnchor();
41122             Ext.destroy(
41123                 this.focusEl,
41124                 this.resizer,
41125                 this.dd,
41126                 this.proxy,
41127                 this.mask
41128             );
41129         }
41130         Ext.Window.superclass.beforeDestroy.call(this);
41131     },
41132
41133     // private
41134     onDestroy : function(){
41135         if(this.manager){
41136             this.manager.unregister(this);
41137         }
41138         Ext.Window.superclass.onDestroy.call(this);
41139     },
41140
41141     // private
41142     initTools : function(){
41143         if(this.minimizable){
41144             this.addTool({
41145                 id: 'minimize',
41146                 handler: this.minimize.createDelegate(this, [])
41147             });
41148         }
41149         if(this.maximizable){
41150             this.addTool({
41151                 id: 'maximize',
41152                 handler: this.maximize.createDelegate(this, [])
41153             });
41154             this.addTool({
41155                 id: 'restore',
41156                 handler: this.restore.createDelegate(this, []),
41157                 hidden:true
41158             });
41159         }
41160         if(this.closable){
41161             this.addTool({
41162                 id: 'close',
41163                 handler: this[this.closeAction].createDelegate(this, [])
41164             });
41165         }
41166     },
41167
41168     // private
41169     resizerAction : function(){
41170         var box = this.proxy.getBox();
41171         this.proxy.hide();
41172         this.window.handleResize(box);
41173         return box;
41174     },
41175
41176     // private
41177     beforeResize : function(){
41178         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?
41179         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);
41180         this.resizeBox = this.el.getBox();
41181     },
41182
41183     // private
41184     updateHandles : function(){
41185         if(Ext.isIE && this.resizer){
41186             this.resizer.syncHandleHeight();
41187             this.el.repaint();
41188         }
41189     },
41190
41191     // private
41192     handleResize : function(box){
41193         var rz = this.resizeBox;
41194         if(rz.x != box.x || rz.y != box.y){
41195             this.updateBox(box);
41196         }else{
41197             this.setSize(box);
41198             if (Ext.isIE6 && Ext.isStrict) {
41199                 this.doLayout();
41200             }
41201         }
41202         this.focus();
41203         this.updateHandles();
41204         this.saveState();
41205     },
41206
41207     /**
41208      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the
41209      * window itself will receive focus.
41210      */
41211     focus : function(){
41212         var f = this.focusEl,
41213             db = this.defaultButton,
41214             t = typeof db,
41215             el,
41216             ct;
41217         if(Ext.isDefined(db)){
41218             if(Ext.isNumber(db) && this.fbar){
41219                 f = this.fbar.items.get(db);
41220             }else if(Ext.isString(db)){
41221                 f = Ext.getCmp(db);
41222             }else{
41223                 f = db;
41224             }
41225             el = f.getEl();
41226             ct = Ext.getDom(this.container);
41227             if (el && ct) {
41228                 if (!Ext.lib.Region.getRegion(ct).contains(Ext.lib.Region.getRegion(el.dom))){
41229                     return;
41230                 }
41231             }
41232         }
41233         f = f || this.focusEl;
41234         f.focus.defer(10, f);
41235     },
41236
41237     /**
41238      * Sets the target element from which the window should animate while opening.
41239      * @param {String/Element} el The target element or id
41240      */
41241     setAnimateTarget : function(el){
41242         el = Ext.get(el);
41243         this.animateTarget = el;
41244     },
41245
41246     // private
41247     beforeShow : function(){
41248         delete this.el.lastXY;
41249         delete this.el.lastLT;
41250         if(this.x === undefined || this.y === undefined){
41251             var xy = this.el.getAlignToXY(this.container, 'c-c');
41252             var pos = this.el.translatePoints(xy[0], xy[1]);
41253             this.x = this.x === undefined? pos.left : this.x;
41254             this.y = this.y === undefined? pos.top : this.y;
41255         }
41256         this.el.setLeftTop(this.x, this.y);
41257
41258         if(this.expandOnShow){
41259             this.expand(false);
41260         }
41261
41262         if(this.modal){
41263             Ext.getBody().addClass('x-body-masked');
41264             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
41265             this.mask.show();
41266         }
41267     },
41268
41269     /**
41270      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
41271      * @param {String/Element} animateTarget (optional) The target element or id from which the window should
41272      * animate while opening (defaults to null with no animation)
41273      * @param {Function} callback (optional) A callback function to call after the window is displayed
41274      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
41275      * @return {Ext.Window} this
41276      */
41277     show : function(animateTarget, cb, scope){
41278         if(!this.rendered){
41279             this.render(Ext.getBody());
41280         }
41281         if(this.hidden === false){
41282             this.toFront();
41283             return this;
41284         }
41285         if(this.fireEvent('beforeshow', this) === false){
41286             return this;
41287         }
41288         if(cb){
41289             this.on('show', cb, scope, {single:true});
41290         }
41291         this.hidden = false;
41292         if(Ext.isDefined(animateTarget)){
41293             this.setAnimateTarget(animateTarget);
41294         }
41295         this.beforeShow();
41296         if(this.animateTarget){
41297             this.animShow();
41298         }else{
41299             this.afterShow();
41300         }
41301         return this;
41302     },
41303
41304     // private
41305     afterShow : function(isAnim){
41306         if (this.isDestroyed){
41307             return false;
41308         }
41309         this.proxy.hide();
41310         this.el.setStyle('display', 'block');
41311         this.el.show();
41312         if(this.maximized){
41313             this.fitContainer();
41314         }
41315         if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
41316             this.cascade(this.setAutoScroll);
41317         }
41318
41319         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
41320             Ext.EventManager.onWindowResize(this.onWindowResize, this);
41321         }
41322         this.doConstrain();
41323         this.doLayout();
41324         if(this.keyMap){
41325             this.keyMap.enable();
41326         }
41327         this.toFront();
41328         this.updateHandles();
41329         if(isAnim && (Ext.isIE || Ext.isWebKit)){
41330             var sz = this.getSize();
41331             this.onResize(sz.width, sz.height);
41332         }
41333         this.onShow();
41334         this.fireEvent('show', this);
41335     },
41336
41337     // private
41338     animShow : function(){
41339         this.proxy.show();
41340         this.proxy.setBox(this.animateTarget.getBox());
41341         this.proxy.setOpacity(0);
41342         var b = this.getBox();
41343         this.el.setStyle('display', 'none');
41344         this.proxy.shift(Ext.apply(b, {
41345             callback: this.afterShow.createDelegate(this, [true], false),
41346             scope: this,
41347             easing: 'easeNone',
41348             duration: 0.25,
41349             opacity: 0.5
41350         }));
41351     },
41352
41353     /**
41354      * Hides the window, setting it to invisible and applying negative offsets.
41355      * @param {String/Element} animateTarget (optional) The target element or id to which the window should
41356      * animate while hiding (defaults to null with no animation)
41357      * @param {Function} callback (optional) A callback function to call after the window is hidden
41358      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
41359      * @return {Ext.Window} this
41360      */
41361     hide : function(animateTarget, cb, scope){
41362         if(this.hidden || this.fireEvent('beforehide', this) === false){
41363             return this;
41364         }
41365         if(cb){
41366             this.on('hide', cb, scope, {single:true});
41367         }
41368         this.hidden = true;
41369         if(animateTarget !== undefined){
41370             this.setAnimateTarget(animateTarget);
41371         }
41372         if(this.modal){
41373             this.mask.hide();
41374             Ext.getBody().removeClass('x-body-masked');
41375         }
41376         if(this.animateTarget){
41377             this.animHide();
41378         }else{
41379             this.el.hide();
41380             this.afterHide();
41381         }
41382         return this;
41383     },
41384
41385     // private
41386     afterHide : function(){
41387         this.proxy.hide();
41388         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
41389             Ext.EventManager.removeResizeListener(this.onWindowResize, this);
41390         }
41391         if(this.keyMap){
41392             this.keyMap.disable();
41393         }
41394         this.onHide();
41395         this.fireEvent('hide', this);
41396     },
41397
41398     // private
41399     animHide : function(){
41400         this.proxy.setOpacity(0.5);
41401         this.proxy.show();
41402         var tb = this.getBox(false);
41403         this.proxy.setBox(tb);
41404         this.el.hide();
41405         this.proxy.shift(Ext.apply(this.animateTarget.getBox(), {
41406             callback: this.afterHide,
41407             scope: this,
41408             duration: 0.25,
41409             easing: 'easeNone',
41410             opacity: 0
41411         }));
41412     },
41413
41414     /**
41415      * Method that is called immediately before the <code>show</code> event is fired.
41416      * Defaults to <code>Ext.emptyFn</code>.
41417      */
41418     onShow : Ext.emptyFn,
41419
41420     /**
41421      * Method that is called immediately before the <code>hide</code> event is fired.
41422      * Defaults to <code>Ext.emptyFn</code>.
41423      */
41424     onHide : Ext.emptyFn,
41425
41426     // private
41427     onWindowResize : function(){
41428         if(this.maximized){
41429             this.fitContainer();
41430         }
41431         if(this.modal){
41432             this.mask.setSize('100%', '100%');
41433             var force = this.mask.dom.offsetHeight;
41434             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
41435         }
41436         this.doConstrain();
41437     },
41438
41439     // private
41440     doConstrain : function(){
41441         if(this.constrain || this.constrainHeader){
41442             var offsets;
41443             if(this.constrain){
41444                 offsets = {
41445                     right:this.el.shadowOffset,
41446                     left:this.el.shadowOffset,
41447                     bottom:this.el.shadowOffset
41448                 };
41449             }else {
41450                 var s = this.getSize();
41451                 offsets = {
41452                     right:-(s.width - 100),
41453                     bottom:-(s.height - 25)
41454                 };
41455             }
41456
41457             var xy = this.el.getConstrainToXY(this.container, true, offsets);
41458             if(xy){
41459                 this.setPosition(xy[0], xy[1]);
41460             }
41461         }
41462     },
41463
41464     // private - used for dragging
41465     ghost : function(cls){
41466         var ghost = this.createGhost(cls);
41467         var box = this.getBox(true);
41468         ghost.setLeftTop(box.x, box.y);
41469         ghost.setWidth(box.width);
41470         this.el.hide();
41471         this.activeGhost = ghost;
41472         return ghost;
41473     },
41474
41475     // private
41476     unghost : function(show, matchPosition){
41477         if(!this.activeGhost) {
41478             return;
41479         }
41480         if(show !== false){
41481             this.el.show();
41482             this.focus.defer(10, this);
41483             if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
41484                 this.cascade(this.setAutoScroll);
41485             }
41486         }
41487         if(matchPosition !== false){
41488             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
41489         }
41490         this.activeGhost.hide();
41491         this.activeGhost.remove();
41492         delete this.activeGhost;
41493     },
41494
41495     /**
41496      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
41497      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
41498      * either the minimize event can be handled or this method can be overridden.
41499      * @return {Ext.Window} this
41500      */
41501     minimize : function(){
41502         this.fireEvent('minimize', this);
41503         return this;
41504     },
41505
41506     /**
41507      * <p>Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s
41508      * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose}
41509      * event is fired before the close happens and will cancel the close action if it returns false.<p>
41510      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
41511      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
41512      * To hide the Window without destroying it, call {@link #hide}.</p>
41513      */
41514     close : function(){
41515         if(this.fireEvent('beforeclose', this) !== false){
41516             if(this.hidden){
41517                 this.doClose();
41518             }else{
41519                 this.hide(null, this.doClose, this);
41520             }
41521         }
41522     },
41523
41524     // private
41525     doClose : function(){
41526         this.fireEvent('close', this);
41527         this.destroy();
41528     },
41529
41530     /**
41531      * Fits the window within its current container and automatically replaces
41532      * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
41533      * Also see {@link #toggleMaximize}.
41534      * @return {Ext.Window} this
41535      */
41536     maximize : function(){
41537         if(!this.maximized){
41538             this.expand(false);
41539             this.restoreSize = this.getSize();
41540             this.restorePos = this.getPosition(true);
41541             if (this.maximizable){
41542                 this.tools.maximize.hide();
41543                 this.tools.restore.show();
41544             }
41545             this.maximized = true;
41546             this.el.disableShadow();
41547
41548             if(this.dd){
41549                 this.dd.lock();
41550             }
41551             if(this.collapsible){
41552                 this.tools.toggle.hide();
41553             }
41554             this.el.addClass('x-window-maximized');
41555             this.container.addClass('x-window-maximized-ct');
41556
41557             this.setPosition(0, 0);
41558             this.fitContainer();
41559             this.fireEvent('maximize', this);
41560         }
41561         return this;
41562     },
41563
41564     /**
41565      * Restores a {@link #maximizable maximized}  window back to its original
41566      * size and position prior to being maximized and also replaces
41567      * the 'restore' tool button with the 'maximize' tool button.
41568      * Also see {@link #toggleMaximize}.
41569      * @return {Ext.Window} this
41570      */
41571     restore : function(){
41572         if(this.maximized){
41573             var t = this.tools;
41574             this.el.removeClass('x-window-maximized');
41575             if(t.restore){
41576                 t.restore.hide();
41577             }
41578             if(t.maximize){
41579                 t.maximize.show();
41580             }
41581             this.setPosition(this.restorePos[0], this.restorePos[1]);
41582             this.setSize(this.restoreSize.width, this.restoreSize.height);
41583             delete this.restorePos;
41584             delete this.restoreSize;
41585             this.maximized = false;
41586             this.el.enableShadow(true);
41587
41588             if(this.dd){
41589                 this.dd.unlock();
41590             }
41591             if(this.collapsible && t.toggle){
41592                 t.toggle.show();
41593             }
41594             this.container.removeClass('x-window-maximized-ct');
41595
41596             this.doConstrain();
41597             this.fireEvent('restore', this);
41598         }
41599         return this;
41600     },
41601
41602     /**
41603      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
41604      * state of the window.
41605      * @return {Ext.Window} this
41606      */
41607     toggleMaximize : function(){
41608         return this[this.maximized ? 'restore' : 'maximize']();
41609     },
41610
41611     // private
41612     fitContainer : function(){
41613         var vs = this.container.getViewSize(false);
41614         this.setSize(vs.width, vs.height);
41615     },
41616
41617     // private
41618     // z-index is managed by the WindowManager and may be overwritten at any time
41619     setZIndex : function(index){
41620         if(this.modal){
41621             this.mask.setStyle('z-index', index);
41622         }
41623         this.el.setZIndex(++index);
41624         index += 5;
41625
41626         if(this.resizer){
41627             this.resizer.proxy.setStyle('z-index', ++index);
41628         }
41629
41630         this.lastZIndex = index;
41631     },
41632
41633     /**
41634      * Aligns the window to the specified element
41635      * @param {Mixed} element The element to align to.
41636      * @param {String} position (optional, defaults to "tl-bl?") The position to align to (see {@link Ext.Element#alignTo} for more details).
41637      * @param {Array} offsets (optional) Offset the positioning by [x, y]
41638      * @return {Ext.Window} this
41639      */
41640     alignTo : function(element, position, offsets){
41641         var xy = this.el.getAlignToXY(element, position, offsets);
41642         this.setPagePosition(xy[0], xy[1]);
41643         return this;
41644     },
41645
41646     /**
41647      * Anchors this window to another element and realigns it when the window is resized or scrolled.
41648      * @param {Mixed} element The element to align to.
41649      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)
41650      * @param {Array} offsets (optional) Offset the positioning by [x, y]
41651      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
41652      * is a number, it is used as the buffer delay (defaults to 50ms).
41653      * @return {Ext.Window} this
41654      */
41655     anchorTo : function(el, alignment, offsets, monitorScroll){
41656         this.clearAnchor();
41657         this.anchorTarget = {
41658             el: el,
41659             alignment: alignment,
41660             offsets: offsets
41661         };
41662
41663         Ext.EventManager.onWindowResize(this.doAnchor, this);
41664         var tm = typeof monitorScroll;
41665         if(tm != 'undefined'){
41666             Ext.EventManager.on(window, 'scroll', this.doAnchor, this,
41667                 {buffer: tm == 'number' ? monitorScroll : 50});
41668         }
41669         return this.doAnchor();
41670     },
41671
41672     /**
41673      * Performs the anchor, using the saved anchorTarget property.
41674      * @return {Ext.Window} this
41675      * @private
41676      */
41677     doAnchor : function(){
41678         var o = this.anchorTarget;
41679         this.alignTo(o.el, o.alignment, o.offsets);
41680         return this;
41681     },
41682
41683     /**
41684      * Removes any existing anchor from this window. See {@link #anchorTo}.
41685      * @return {Ext.Window} this
41686      */
41687     clearAnchor : function(){
41688         if(this.anchorTarget){
41689             Ext.EventManager.removeResizeListener(this.doAnchor, this);
41690             Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
41691             delete this.anchorTarget;
41692         }
41693         return this;
41694     },
41695
41696     /**
41697      * Brings this window to the front of any other visible windows
41698      * @param {Boolean} e (optional) Specify <tt>false</tt> to prevent the window from being focused.
41699      * @return {Ext.Window} this
41700      */
41701     toFront : function(e){
41702         if(this.manager.bringToFront(this)){
41703             if(!e || !e.getTarget().focus){
41704                 this.focus();
41705             }
41706         }
41707         return this;
41708     },
41709
41710     /**
41711      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also
41712      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred. This method is
41713      * called internally by {@link Ext.WindowMgr}.
41714      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)
41715      */
41716     setActive : function(active){
41717         if(active){
41718             if(!this.maximized){
41719                 this.el.enableShadow(true);
41720             }
41721             this.fireEvent('activate', this);
41722         }else{
41723             this.el.disableShadow();
41724             this.fireEvent('deactivate', this);
41725         }
41726     },
41727
41728     /**
41729      * Sends this window to the back of (lower z-index than) any other visible windows
41730      * @return {Ext.Window} this
41731      */
41732     toBack : function(){
41733         this.manager.sendToBack(this);
41734         return this;
41735     },
41736
41737     /**
41738      * Centers this window in the viewport
41739      * @return {Ext.Window} this
41740      */
41741     center : function(){
41742         var xy = this.el.getAlignToXY(this.container, 'c-c');
41743         this.setPagePosition(xy[0], xy[1]);
41744         return this;
41745     }
41746
41747     /**
41748      * @cfg {Boolean} autoWidth @hide
41749      **/
41750 });
41751 Ext.reg('window', Ext.Window);
41752
41753 // private - custom Window DD implementation
41754 Ext.Window.DD = function(win){
41755     this.win = win;
41756     Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);
41757     this.setHandleElId(win.header.id);
41758     this.scroll = false;
41759 };
41760
41761 Ext.extend(Ext.Window.DD, Ext.dd.DD, {
41762     moveOnly:true,
41763     headerOffsets:[100, 25],
41764     startDrag : function(){
41765         var w = this.win;
41766         this.proxy = w.ghost();
41767         if(w.constrain !== false){
41768             var so = w.el.shadowOffset;
41769             this.constrainTo(w.container, {right: so, left: so, bottom: so});
41770         }else if(w.constrainHeader !== false){
41771             var s = this.proxy.getSize();
41772             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});
41773         }
41774     },
41775     b4Drag : Ext.emptyFn,
41776
41777     onDrag : function(e){
41778         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
41779     },
41780
41781     endDrag : function(e){
41782         this.win.unghost();
41783         this.win.saveState();
41784     }
41785 });
41786 /**
41787  * @class Ext.WindowGroup
41788  * An object that manages a group of {@link Ext.Window} instances and provides z-order management
41789  * and window activation behavior.
41790  * @constructor
41791  */
41792 Ext.WindowGroup = function(){
41793     var list = {};
41794     var accessList = [];
41795     var front = null;
41796
41797     // private
41798     var sortWindows = function(d1, d2){
41799         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
41800     };
41801
41802     // private
41803     var orderWindows = function(){
41804         var a = accessList, len = a.length;
41805         if(len > 0){
41806             a.sort(sortWindows);
41807             var seed = a[0].manager.zseed;
41808             for(var i = 0; i < len; i++){
41809                 var win = a[i];
41810                 if(win && !win.hidden){
41811                     win.setZIndex(seed + (i*10));
41812                 }
41813             }
41814         }
41815         activateLast();
41816     };
41817
41818     // private
41819     var setActiveWin = function(win){
41820         if(win != front){
41821             if(front){
41822                 front.setActive(false);
41823             }
41824             front = win;
41825             if(win){
41826                 win.setActive(true);
41827             }
41828         }
41829     };
41830
41831     // private
41832     var activateLast = function(){
41833         for(var i = accessList.length-1; i >=0; --i) {
41834             if(!accessList[i].hidden){
41835                 setActiveWin(accessList[i]);
41836                 return;
41837             }
41838         }
41839         // none to activate
41840         setActiveWin(null);
41841     };
41842
41843     return {
41844         /**
41845          * The starting z-index for windows in this WindowGroup (defaults to 9000)
41846          * @type Number The z-index value
41847          */
41848         zseed : 9000,
41849
41850         /**
41851          * <p>Registers a {@link Ext.Window Window} with this WindowManager. This should not
41852          * need to be called under normal circumstances. Windows are automatically registered
41853          * with a {@link Ext.Window#manager manager} at construction time.</p>
41854          * <p>Where this may be useful is moving Windows between two WindowManagers. For example,
41855          * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
41856          * WindowManager in the desktop sample app:</p><code><pre>
41857 var msgWin = Ext.MessageBox.getDialog();
41858 MyDesktop.getDesktop().getManager().register(msgWin);
41859 </pre></code>
41860          * @param {Window} win The Window to register.
41861          */
41862         register : function(win){
41863             if(win.manager){
41864                 win.manager.unregister(win);
41865             }
41866             win.manager = this;
41867
41868             list[win.id] = win;
41869             accessList.push(win);
41870             win.on('hide', activateLast);
41871         },
41872
41873         /**
41874          * <p>Unregisters a {@link Ext.Window Window} from this WindowManager. This should not
41875          * need to be called. Windows are automatically unregistered upon destruction.
41876          * See {@link #register}.</p>
41877          * @param {Window} win The Window to unregister.
41878          */
41879         unregister : function(win){
41880             delete win.manager;
41881             delete list[win.id];
41882             win.un('hide', activateLast);
41883             accessList.remove(win);
41884         },
41885
41886         /**
41887          * Gets a registered window by id.
41888          * @param {String/Object} id The id of the window or a {@link Ext.Window} instance
41889          * @return {Ext.Window}
41890          */
41891         get : function(id){
41892             return typeof id == "object" ? id : list[id];
41893         },
41894
41895         /**
41896          * Brings the specified window to the front of any other active windows in this WindowGroup.
41897          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
41898          * @return {Boolean} True if the dialog was brought to the front, else false
41899          * if it was already in front
41900          */
41901         bringToFront : function(win){
41902             win = this.get(win);
41903             if(win != front){
41904                 win._lastAccess = new Date().getTime();
41905                 orderWindows();
41906                 return true;
41907             }
41908             return false;
41909         },
41910
41911         /**
41912          * Sends the specified window to the back of other active windows in this WindowGroup.
41913          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
41914          * @return {Ext.Window} The window
41915          */
41916         sendToBack : function(win){
41917             win = this.get(win);
41918             win._lastAccess = -(new Date().getTime());
41919             orderWindows();
41920             return win;
41921         },
41922
41923         /**
41924          * Hides all windows in this WindowGroup.
41925          */
41926         hideAll : function(){
41927             for(var id in list){
41928                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
41929                     list[id].hide();
41930                 }
41931             }
41932         },
41933
41934         /**
41935          * Gets the currently-active window in this WindowGroup.
41936          * @return {Ext.Window} The active window
41937          */
41938         getActive : function(){
41939             return front;
41940         },
41941
41942         /**
41943          * Returns zero or more windows in this WindowGroup using the custom search function passed to this method.
41944          * The function should accept a single {@link Ext.Window} reference as its only argument and should
41945          * return true if the window matches the search criteria, otherwise it should return false.
41946          * @param {Function} fn The search function
41947          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Window being tested.
41948          * that gets passed to the function if not specified)
41949          * @return {Array} An array of zero or more matching windows
41950          */
41951         getBy : function(fn, scope){
41952             var r = [];
41953             for(var i = accessList.length-1; i >=0; --i) {
41954                 var win = accessList[i];
41955                 if(fn.call(scope||win, win) !== false){
41956                     r.push(win);
41957                 }
41958             }
41959             return r;
41960         },
41961
41962         /**
41963          * Executes the specified function once for every window in this WindowGroup, passing each
41964          * window as the only parameter. Returning false from the function will stop the iteration.
41965          * @param {Function} fn The function to execute for each item
41966          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Window in the iteration.
41967          */
41968         each : function(fn, scope){
41969             for(var id in list){
41970                 if(list[id] && typeof list[id] != "function"){
41971                     if(fn.call(scope || list[id], list[id]) === false){
41972                         return;
41973                     }
41974                 }
41975             }
41976         }
41977     };
41978 };
41979
41980
41981 /**
41982  * @class Ext.WindowMgr
41983  * @extends Ext.WindowGroup
41984  * The default global window group that is available automatically.  To have more than one group of windows
41985  * with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
41986  * @singleton
41987  */
41988 Ext.WindowMgr = new Ext.WindowGroup();/**
41989  * @class Ext.MessageBox
41990  * <p>Utility class for generating different styles of message boxes.  The alias Ext.Msg can also be used.<p/>
41991  * <p>Note that the MessageBox is asynchronous.  Unlike a regular JavaScript <code>alert</code> (which will halt
41992  * browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code
41993  * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function
41994  * (see the <code>function</code> parameter for {@link #show} for more details).</p>
41995  * <p>Example usage:</p>
41996  *<pre><code>
41997 // Basic alert:
41998 Ext.Msg.alert('Status', 'Changes saved successfully.');
41999
42000 // Prompt for user data and process the result using a callback:
42001 Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
42002     if (btn == 'ok'){
42003         // process text value and close...
42004     }
42005 });
42006
42007 // Show a dialog using config options:
42008 Ext.Msg.show({
42009    title:'Save Changes?',
42010    msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',
42011    buttons: Ext.Msg.YESNOCANCEL,
42012    fn: processResult,
42013    animEl: 'elId',
42014    icon: Ext.MessageBox.QUESTION
42015 });
42016 </code></pre>
42017  * @singleton
42018  */
42019 Ext.MessageBox = function(){
42020     var dlg, opt, mask, waitTimer,
42021         bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl,
42022         buttons, activeTextEl, bwidth, bufferIcon = '', iconCls = '',
42023         buttonNames = ['ok', 'yes', 'no', 'cancel'];
42024
42025     // private
42026     var handleButton = function(button){
42027         buttons[button].blur();
42028         if(dlg.isVisible()){
42029             dlg.hide();
42030             handleHide();
42031             Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);
42032         }
42033     };
42034
42035     // private
42036     var handleHide = function(){
42037         if(opt && opt.cls){
42038             dlg.el.removeClass(opt.cls);
42039         }
42040         progressBar.reset();        
42041     };
42042
42043     // private
42044     var handleEsc = function(d, k, e){
42045         if(opt && opt.closable !== false){
42046             dlg.hide();
42047             handleHide();
42048         }
42049         if(e){
42050             e.stopEvent();
42051         }
42052     };
42053
42054     // private
42055     var updateButtons = function(b){
42056         var width = 0,
42057             cfg;
42058         if(!b){
42059             Ext.each(buttonNames, function(name){
42060                 buttons[name].hide();
42061             });
42062             return width;
42063         }
42064         dlg.footer.dom.style.display = '';
42065         Ext.iterate(buttons, function(name, btn){
42066             cfg = b[name];
42067             if(cfg){
42068                 btn.show();
42069                 btn.setText(Ext.isString(cfg) ? cfg : Ext.MessageBox.buttonText[name]);
42070                 width += btn.getEl().getWidth() + 15;
42071             }else{
42072                 btn.hide();
42073             }
42074         });
42075         return width;
42076     };
42077
42078     return {
42079         /**
42080          * Returns a reference to the underlying {@link Ext.Window} element
42081          * @return {Ext.Window} The window
42082          */
42083         getDialog : function(titleText){
42084            if(!dlg){
42085                 var btns = [];
42086                 
42087                 buttons = {};
42088                 Ext.each(buttonNames, function(name){
42089                     btns.push(buttons[name] = new Ext.Button({
42090                         text: this.buttonText[name],
42091                         handler: handleButton.createCallback(name),
42092                         hideMode: 'offsets'
42093                     }));
42094                 }, this);
42095                 dlg = new Ext.Window({
42096                     autoCreate : true,
42097                     title:titleText,
42098                     resizable:false,
42099                     constrain:true,
42100                     constrainHeader:true,
42101                     minimizable : false,
42102                     maximizable : false,
42103                     stateful: false,
42104                     modal: true,
42105                     shim:true,
42106                     buttonAlign:"center",
42107                     width:400,
42108                     height:100,
42109                     minHeight: 80,
42110                     plain:true,
42111                     footer:true,
42112                     closable:true,
42113                     close : function(){
42114                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
42115                             handleButton("no");
42116                         }else{
42117                             handleButton("cancel");
42118                         }
42119                     },
42120                     fbar: new Ext.Toolbar({
42121                         items: btns,
42122                         enableOverflow: false
42123                     })
42124                 });
42125                 dlg.render(document.body);
42126                 dlg.getEl().addClass('x-window-dlg');
42127                 mask = dlg.mask;
42128                 bodyEl = dlg.body.createChild({
42129                     html:'<div class="ext-mb-icon"></div><div class="ext-mb-content"><span class="ext-mb-text"></span><br /><div class="ext-mb-fix-cursor"><input type="text" class="ext-mb-input" /><textarea class="ext-mb-textarea"></textarea></div></div>'
42130                 });
42131                 iconEl = Ext.get(bodyEl.dom.firstChild);
42132                 var contentEl = bodyEl.dom.childNodes[1];
42133                 msgEl = Ext.get(contentEl.firstChild);
42134                 textboxEl = Ext.get(contentEl.childNodes[2].firstChild);
42135                 textboxEl.enableDisplayMode();
42136                 textboxEl.addKeyListener([10,13], function(){
42137                     if(dlg.isVisible() && opt && opt.buttons){
42138                         if(opt.buttons.ok){
42139                             handleButton("ok");
42140                         }else if(opt.buttons.yes){
42141                             handleButton("yes");
42142                         }
42143                     }
42144                 });
42145                 textareaEl = Ext.get(contentEl.childNodes[2].childNodes[1]);
42146                 textareaEl.enableDisplayMode();
42147                 progressBar = new Ext.ProgressBar({
42148                     renderTo:bodyEl
42149                 });
42150                bodyEl.createChild({cls:'x-clear'});
42151             }
42152             return dlg;
42153         },
42154
42155         /**
42156          * Updates the message box body text
42157          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
42158          * the XHTML-compliant non-breaking space character '&amp;#160;')
42159          * @return {Ext.MessageBox} this
42160          */
42161         updateText : function(text){
42162             if(!dlg.isVisible() && !opt.width){
42163                 dlg.setSize(this.maxWidth, 100); // resize first so content is never clipped from previous shows
42164             }
42165             msgEl.update(text || '&#160;');
42166
42167             var iw = iconCls != '' ? (iconEl.getWidth() + iconEl.getMargins('lr')) : 0,
42168                 mw = msgEl.getWidth() + msgEl.getMargins('lr'),
42169                 fw = dlg.getFrameWidth('lr'),
42170                 bw = dlg.body.getFrameWidth('lr'),
42171                 w;
42172                 
42173             if (Ext.isIE && iw > 0){
42174                 //3 pixels get subtracted in the icon CSS for an IE margin issue,
42175                 //so we have to add it back here for the overall width to be consistent
42176                 iw += 3;
42177             }
42178             w = Math.max(Math.min(opt.width || iw+mw+fw+bw, opt.maxWidth || this.maxWidth),
42179                     Math.max(opt.minWidth || this.minWidth, bwidth || 0));
42180
42181             if(opt.prompt === true){
42182                 activeTextEl.setWidth(w-iw-fw-bw);
42183             }
42184             if(opt.progress === true || opt.wait === true){
42185                 progressBar.setSize(w-iw-fw-bw);
42186             }
42187             if(Ext.isIE && w == bwidth){
42188                 w += 4; //Add offset when the content width is smaller than the buttons.    
42189             }
42190             dlg.setSize(w, 'auto').center();
42191             return this;
42192         },
42193
42194         /**
42195          * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
42196          * initiated via {@link Ext.MessageBox#progress} or {@link Ext.MessageBox#wait},
42197          * or by calling {@link Ext.MessageBox#show} with progress: true.
42198          * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)
42199          * @param {String} progressText The progress text to display inside the progress bar (defaults to '')
42200          * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined
42201          * so that any existing body text will not get overwritten by default unless a new value is passed in)
42202          * @return {Ext.MessageBox} this
42203          */
42204         updateProgress : function(value, progressText, msg){
42205             progressBar.updateProgress(value, progressText);
42206             if(msg){
42207                 this.updateText(msg);
42208             }
42209             return this;
42210         },
42211
42212         /**
42213          * Returns true if the message box is currently displayed
42214          * @return {Boolean} True if the message box is visible, else false
42215          */
42216         isVisible : function(){
42217             return dlg && dlg.isVisible();
42218         },
42219
42220         /**
42221          * Hides the message box if it is displayed
42222          * @return {Ext.MessageBox} this
42223          */
42224         hide : function(){
42225             var proxy = dlg ? dlg.activeGhost : null;
42226             if(this.isVisible() || proxy){
42227                 dlg.hide();
42228                 handleHide();
42229                 if (proxy){
42230                     // unghost is a private function, but i saw no better solution
42231                     // to fix the locking problem when dragging while it closes
42232                     dlg.unghost(false, false);
42233                 } 
42234             }
42235             return this;
42236         },
42237
42238         /**
42239          * Displays a new message box, or reinitializes an existing message box, based on the config options
42240          * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,
42241          * although those calls are basic shortcuts and do not support all of the config options allowed here.
42242          * @param {Object} config The following config options are supported: <ul>
42243          * <li><b>animEl</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it
42244          * opens and closes (defaults to undefined)</div></li>
42245          * <li><b>buttons</b> : Object/Boolean<div class="sub-desc">A button config object (e.g., Ext.MessageBox.OKCANCEL or {ok:'Foo',
42246          * cancel:'Bar'}), or false to not show any buttons (defaults to false)</div></li>
42247          * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that
42248          * progress and wait dialogs will ignore this property and always hide the close button as they can only
42249          * be closed programmatically.</div></li>
42250          * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>
42251          * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea
42252          * if displayed (defaults to 75)</div></li>
42253          * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either
42254          * by clicking on the configured buttons, or on the dialog close button, or by pressing
42255          * the return button to enter input.
42256          * <p>Progress and wait dialogs will ignore this option since they do not respond to user
42257          * actions and can only be closed programmatically, so any required function should be called
42258          * by the same code after it closes the dialog. Parameters passed:<ul>
42259          * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>
42260          * <li><tt>ok</tt></li>
42261          * <li><tt>yes</tt></li>
42262          * <li><tt>no</tt></li>
42263          * <li><tt>cancel</tt></li>
42264          * </ul></div></div></li>
42265          * <li><b>text</b> : String<div class="sub-desc">Value of the input field if either <tt><a href="#show-option-prompt" ext:member="show-option-prompt" ext:cls="Ext.MessageBox">prompt</a></tt>
42266          * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.MessageBox">multiline</a></tt> is true</div></li>
42267          * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>
42268          * </ul></p></div></li>
42269          * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>
42270          * <li><b>icon</b> : String<div class="sub-desc">A CSS class that provides a background image to be used as the body icon for the
42271          * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>
42272          * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.Window#iconCls} to
42273          * add an optional header icon (defaults to '')</div></li>
42274          * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>
42275          * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>
42276          * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is
42277          * displayed (defaults to true)</div></li>
42278          * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the
42279          * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>
42280          * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">
42281          * True to prompt the user to enter multi-line text (defaults to false)</div></li>
42282          * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
42283          * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>
42284          * <li><a id="show-option-prompt"></a><b>prompt</b> : Boolean<div class="sub-desc">True to prompt the user to enter single-line text (defaults to false)</div></li>
42285          * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>
42286          * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>
42287          * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>
42288          * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
42289          * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>
42290          * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>
42291          * </ul>
42292          * Example usage:
42293          * <pre><code>
42294 Ext.Msg.show({
42295    title: 'Address',
42296    msg: 'Please enter your address:',
42297    width: 300,
42298    buttons: Ext.MessageBox.OKCANCEL,
42299    multiline: true,
42300    fn: saveAddress,
42301    animEl: 'addAddressBtn',
42302    icon: Ext.MessageBox.INFO
42303 });
42304 </code></pre>
42305          * @return {Ext.MessageBox} this
42306          */
42307         show : function(options){
42308             if(this.isVisible()){
42309                 this.hide();
42310             }
42311             opt = options;
42312             var d = this.getDialog(opt.title || "&#160;");
42313
42314             d.setTitle(opt.title || "&#160;");
42315             var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);
42316             d.tools.close.setDisplayed(allowClose);
42317             activeTextEl = textboxEl;
42318             opt.prompt = opt.prompt || (opt.multiline ? true : false);
42319             if(opt.prompt){
42320                 if(opt.multiline){
42321                     textboxEl.hide();
42322                     textareaEl.show();
42323                     textareaEl.setHeight(Ext.isNumber(opt.multiline) ? opt.multiline : this.defaultTextHeight);
42324                     activeTextEl = textareaEl;
42325                 }else{
42326                     textboxEl.show();
42327                     textareaEl.hide();
42328                 }
42329             }else{
42330                 textboxEl.hide();
42331                 textareaEl.hide();
42332             }
42333             activeTextEl.dom.value = opt.value || "";
42334             if(opt.prompt){
42335                 d.focusEl = activeTextEl;
42336             }else{
42337                 var bs = opt.buttons;
42338                 var db = null;
42339                 if(bs && bs.ok){
42340                     db = buttons["ok"];
42341                 }else if(bs && bs.yes){
42342                     db = buttons["yes"];
42343                 }
42344                 if (db){
42345                     d.focusEl = db;
42346                 }
42347             }
42348             if(opt.iconCls){
42349               d.setIconClass(opt.iconCls);
42350             }
42351             this.setIcon(Ext.isDefined(opt.icon) ? opt.icon : bufferIcon);
42352             bwidth = updateButtons(opt.buttons);
42353             progressBar.setVisible(opt.progress === true || opt.wait === true);
42354             this.updateProgress(0, opt.progressText);
42355             this.updateText(opt.msg);
42356             if(opt.cls){
42357                 d.el.addClass(opt.cls);
42358             }
42359             d.proxyDrag = opt.proxyDrag === true;
42360             d.modal = opt.modal !== false;
42361             d.mask = opt.modal !== false ? mask : false;
42362             if(!d.isVisible()){
42363                 // force it to the end of the z-index stack so it gets a cursor in FF
42364                 document.body.appendChild(dlg.el.dom);
42365                 d.setAnimateTarget(opt.animEl);
42366                 //workaround for window internally enabling keymap in afterShow
42367                 d.on('show', function(){
42368                     if(allowClose === true){
42369                         d.keyMap.enable();
42370                     }else{
42371                         d.keyMap.disable();
42372                     }
42373                 }, this, {single:true});
42374                 d.show(opt.animEl);
42375             }
42376             if(opt.wait === true){
42377                 progressBar.wait(opt.waitConfig);
42378             }
42379             return this;
42380         },
42381
42382         /**
42383          * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default
42384          * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
42385          * to clear any existing icon. This method must be called before the MessageBox is shown.
42386          * The following built-in icon classes are supported, but you can also pass in a custom class name:
42387          * <pre>
42388 Ext.MessageBox.INFO
42389 Ext.MessageBox.WARNING
42390 Ext.MessageBox.QUESTION
42391 Ext.MessageBox.ERROR
42392          *</pre>
42393          * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon
42394          * @return {Ext.MessageBox} this
42395          */
42396         setIcon : function(icon){
42397             if(!dlg){
42398                 bufferIcon = icon;
42399                 return;
42400             }
42401             bufferIcon = undefined;
42402             if(icon && icon != ''){
42403                 iconEl.removeClass('x-hidden');
42404                 iconEl.replaceClass(iconCls, icon);
42405                 bodyEl.addClass('x-dlg-icon');
42406                 iconCls = icon;
42407             }else{
42408                 iconEl.replaceClass(iconCls, 'x-hidden');
42409                 bodyEl.removeClass('x-dlg-icon');
42410                 iconCls = '';
42411             }
42412             return this;
42413         },
42414
42415         /**
42416          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
42417          * the user.  You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress}
42418          * and closing the message box when the process is complete.
42419          * @param {String} title The title bar text
42420          * @param {String} msg The message box body text
42421          * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')
42422          * @return {Ext.MessageBox} this
42423          */
42424         progress : function(title, msg, progressText){
42425             this.show({
42426                 title : title,
42427                 msg : msg,
42428                 buttons: false,
42429                 progress:true,
42430                 closable:false,
42431                 minWidth: this.minProgressWidth,
42432                 progressText: progressText
42433             });
42434             return this;
42435         },
42436
42437         /**
42438          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
42439          * interaction while waiting for a long-running process to complete that does not have defined intervals.
42440          * You are responsible for closing the message box when the process is complete.
42441          * @param {String} msg The message box body text
42442          * @param {String} title (optional) The title bar text
42443          * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object
42444          * @return {Ext.MessageBox} this
42445          */
42446         wait : function(msg, title, config){
42447             this.show({
42448                 title : title,
42449                 msg : msg,
42450                 buttons: false,
42451                 closable:false,
42452                 wait:true,
42453                 modal:true,
42454                 minWidth: this.minProgressWidth,
42455                 waitConfig: config
42456             });
42457             return this;
42458         },
42459
42460         /**
42461          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).
42462          * If a callback function is passed it will be called after the user clicks the button, and the
42463          * id of the button that was clicked will be passed as the only parameter to the callback
42464          * (could also be the top-right close button).
42465          * @param {String} title The title bar text
42466          * @param {String} msg The message box body text
42467          * @param {Function} fn (optional) The callback function invoked after the message box is closed
42468          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
42469          * @return {Ext.MessageBox} this
42470          */
42471         alert : function(title, msg, fn, scope){
42472             this.show({
42473                 title : title,
42474                 msg : msg,
42475                 buttons: this.OK,
42476                 fn: fn,
42477                 scope : scope,
42478                 minWidth: this.minWidth
42479             });
42480             return this;
42481         },
42482
42483         /**
42484          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
42485          * If a callback function is passed it will be called after the user clicks either button,
42486          * and the id of the button that was clicked will be passed as the only parameter to the callback
42487          * (could also be the top-right close button).
42488          * @param {String} title The title bar text
42489          * @param {String} msg The message box body text
42490          * @param {Function} fn (optional) The callback function invoked after the message box is closed
42491          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
42492          * @return {Ext.MessageBox} this
42493          */
42494         confirm : function(title, msg, fn, scope){
42495             this.show({
42496                 title : title,
42497                 msg : msg,
42498                 buttons: this.YESNO,
42499                 fn: fn,
42500                 scope : scope,
42501                 icon: this.QUESTION,
42502                 minWidth: this.minWidth
42503             });
42504             return this;
42505         },
42506
42507         /**
42508          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
42509          * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user
42510          * clicks either button, and the id of the button that was clicked (could also be the top-right
42511          * close button) and the text that was entered will be passed as the two parameters to the callback.
42512          * @param {String} title The title bar text
42513          * @param {String} msg The message box body text
42514          * @param {Function} fn (optional) The callback function invoked after the message box is closed
42515          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
42516          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
42517          * property, or the height in pixels to create the textbox (defaults to false / single-line)
42518          * @param {String} value (optional) Default value of the text input element (defaults to '')
42519          * @return {Ext.MessageBox} this
42520          */
42521         prompt : function(title, msg, fn, scope, multiline, value){
42522             this.show({
42523                 title : title,
42524                 msg : msg,
42525                 buttons: this.OKCANCEL,
42526                 fn: fn,
42527                 minWidth: this.minPromptWidth,
42528                 scope : scope,
42529                 prompt:true,
42530                 multiline: multiline,
42531                 value: value
42532             });
42533             return this;
42534         },
42535
42536         /**
42537          * Button config that displays a single OK button
42538          * @type Object
42539          */
42540         OK : {ok:true},
42541         /**
42542          * Button config that displays a single Cancel button
42543          * @type Object
42544          */
42545         CANCEL : {cancel:true},
42546         /**
42547          * Button config that displays OK and Cancel buttons
42548          * @type Object
42549          */
42550         OKCANCEL : {ok:true, cancel:true},
42551         /**
42552          * Button config that displays Yes and No buttons
42553          * @type Object
42554          */
42555         YESNO : {yes:true, no:true},
42556         /**
42557          * Button config that displays Yes, No and Cancel buttons
42558          * @type Object
42559          */
42560         YESNOCANCEL : {yes:true, no:true, cancel:true},
42561         /**
42562          * The CSS class that provides the INFO icon image
42563          * @type String
42564          */
42565         INFO : 'ext-mb-info',
42566         /**
42567          * The CSS class that provides the WARNING icon image
42568          * @type String
42569          */
42570         WARNING : 'ext-mb-warning',
42571         /**
42572          * The CSS class that provides the QUESTION icon image
42573          * @type String
42574          */
42575         QUESTION : 'ext-mb-question',
42576         /**
42577          * The CSS class that provides the ERROR icon image
42578          * @type String
42579          */
42580         ERROR : 'ext-mb-error',
42581
42582         /**
42583          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
42584          * @type Number
42585          */
42586         defaultTextHeight : 75,
42587         /**
42588          * The maximum width in pixels of the message box (defaults to 600)
42589          * @type Number
42590          */
42591         maxWidth : 600,
42592         /**
42593          * The minimum width in pixels of the message box (defaults to 100)
42594          * @type Number
42595          */
42596         minWidth : 100,
42597         /**
42598          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
42599          * for setting a different minimum width than text-only dialogs may need (defaults to 250).
42600          * @type Number
42601          */
42602         minProgressWidth : 250,
42603         /**
42604          * The minimum width in pixels of the message box if it is a prompt dialog.  This is useful
42605          * for setting a different minimum width than text-only dialogs may need (defaults to 250).
42606          * @type Number
42607          */
42608         minPromptWidth: 250,
42609         /**
42610          * An object containing the default button text strings that can be overriden for localized language support.
42611          * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific
42612          * resource file for handling language support across the framework.
42613          * Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french
42614          * @type Object
42615          */
42616         buttonText : {
42617             ok : "OK",
42618             cancel : "Cancel",
42619             yes : "Yes",
42620             no : "No"
42621         }
42622     };
42623 }();
42624
42625 /**
42626  * Shorthand for {@link Ext.MessageBox}
42627  */
42628 Ext.Msg = Ext.MessageBox;/**
42629  * @class Ext.dd.PanelProxy
42630  * A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally
42631  * for the Panel's drag drop implementation, and should never need to be created directly.
42632  * @constructor
42633  * @param panel The {@link Ext.Panel} to proxy for
42634  * @param config Configuration options
42635  */
42636 Ext.dd.PanelProxy = function(panel, config){
42637     this.panel = panel;
42638     this.id = this.panel.id +'-ddproxy';
42639     Ext.apply(this, config);
42640 };
42641
42642 Ext.dd.PanelProxy.prototype = {
42643     /**
42644      * @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,
42645      * false to drag with no proxy (defaults to true).
42646      */
42647     insertProxy : true,
42648
42649     // private overrides
42650     setStatus : Ext.emptyFn,
42651     reset : Ext.emptyFn,
42652     update : Ext.emptyFn,
42653     stop : Ext.emptyFn,
42654     sync: Ext.emptyFn,
42655
42656     /**
42657      * Gets the proxy's element
42658      * @return {Element} The proxy's element
42659      */
42660     getEl : function(){
42661         return this.ghost;
42662     },
42663
42664     /**
42665      * Gets the proxy's ghost element
42666      * @return {Element} The proxy's ghost element
42667      */
42668     getGhost : function(){
42669         return this.ghost;
42670     },
42671
42672     /**
42673      * Gets the proxy's element
42674      * @return {Element} The proxy's element
42675      */
42676     getProxy : function(){
42677         return this.proxy;
42678     },
42679
42680     /**
42681      * Hides the proxy
42682      */
42683     hide : function(){
42684         if(this.ghost){
42685             if(this.proxy){
42686                 this.proxy.remove();
42687                 delete this.proxy;
42688             }
42689             this.panel.el.dom.style.display = '';
42690             this.ghost.remove();
42691             delete this.ghost;
42692         }
42693     },
42694
42695     /**
42696      * Shows the proxy
42697      */
42698     show : function(){
42699         if(!this.ghost){
42700             this.ghost = this.panel.createGhost(undefined, undefined, Ext.getBody());
42701             this.ghost.setXY(this.panel.el.getXY());
42702             if(this.insertProxy){
42703                 this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});
42704                 this.proxy.setSize(this.panel.getSize());
42705             }
42706             this.panel.el.dom.style.display = 'none';
42707         }
42708     },
42709
42710     // private
42711     repair : function(xy, callback, scope){
42712         this.hide();
42713         if(typeof callback == "function"){
42714             callback.call(scope || this);
42715         }
42716     },
42717
42718     /**
42719      * Moves the proxy to a different position in the DOM.  This is typically called while dragging the Panel
42720      * to keep the proxy sync'd to the Panel's location.
42721      * @param {HTMLElement} parentNode The proxy's parent DOM node
42722      * @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults
42723      * to the parent's last child if not specified)
42724      */
42725     moveProxy : function(parentNode, before){
42726         if(this.proxy){
42727             parentNode.insertBefore(this.proxy.dom, before);
42728         }
42729     }
42730 };
42731
42732 // private - DD implementation for Panels
42733 Ext.Panel.DD = function(panel, cfg){
42734     this.panel = panel;
42735     this.dragData = {panel: panel};
42736     this.proxy = new Ext.dd.PanelProxy(panel, cfg);
42737     Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);
42738     var h = panel.header;
42739     if(h){
42740         this.setHandleElId(h.id);
42741     }
42742     (h ? h : this.panel.body).setStyle('cursor', 'move');
42743     this.scroll = false;
42744 };
42745
42746 Ext.extend(Ext.Panel.DD, Ext.dd.DragSource, {
42747     showFrame: Ext.emptyFn,
42748     startDrag: Ext.emptyFn,
42749     b4StartDrag: function(x, y) {
42750         this.proxy.show();
42751     },
42752     b4MouseDown: function(e) {
42753         var x = e.getPageX();
42754         var y = e.getPageY();
42755         this.autoOffset(x, y);
42756     },
42757     onInitDrag : function(x, y){
42758         this.onStartDrag(x, y);
42759         return true;
42760     },
42761     createFrame : Ext.emptyFn,
42762     getDragEl : function(e){
42763         return this.proxy.ghost.dom;
42764     },
42765     endDrag : function(e){
42766         this.proxy.hide();
42767         this.panel.saveState();
42768     },
42769
42770     autoOffset : function(x, y) {
42771         x -= this.startPageX;
42772         y -= this.startPageY;
42773         this.setDelta(x, y);
42774     }
42775 });/**
42776  * @class Ext.state.Provider
42777  * Abstract base class for state provider implementations. This class provides methods
42778  * for encoding and decoding <b>typed</b> variables including dates and defines the
42779  * Provider interface.
42780  */
42781 Ext.state.Provider = function(){
42782     /**
42783      * @event statechange
42784      * Fires when a state change occurs.
42785      * @param {Provider} this This state provider
42786      * @param {String} key The state key which was changed
42787      * @param {String} value The encoded value for the state
42788      */
42789     this.addEvents("statechange");
42790     this.state = {};
42791     Ext.state.Provider.superclass.constructor.call(this);
42792 };
42793 Ext.extend(Ext.state.Provider, Ext.util.Observable, {
42794     /**
42795      * Returns the current value for a key
42796      * @param {String} name The key name
42797      * @param {Mixed} defaultValue A default value to return if the key's value is not found
42798      * @return {Mixed} The state data
42799      */
42800     get : function(name, defaultValue){
42801         return typeof this.state[name] == "undefined" ?
42802             defaultValue : this.state[name];
42803     },
42804
42805     /**
42806      * Clears a value from the state
42807      * @param {String} name The key name
42808      */
42809     clear : function(name){
42810         delete this.state[name];
42811         this.fireEvent("statechange", this, name, null);
42812     },
42813
42814     /**
42815      * Sets the value for a key
42816      * @param {String} name The key name
42817      * @param {Mixed} value The value to set
42818      */
42819     set : function(name, value){
42820         this.state[name] = value;
42821         this.fireEvent("statechange", this, name, value);
42822     },
42823
42824     /**
42825      * Decodes a string previously encoded with {@link #encodeValue}.
42826      * @param {String} value The value to decode
42827      * @return {Mixed} The decoded value
42828      */
42829     decodeValue : function(cookie){
42830         var re = /^(a|n|d|b|s|o)\:(.*)$/;
42831         var matches = re.exec(unescape(cookie));
42832         if(!matches || !matches[1]) return; // non state cookie
42833         var type = matches[1];
42834         var v = matches[2];
42835         switch(type){
42836             case "n":
42837                 return parseFloat(v);
42838             case "d":
42839                 return new Date(Date.parse(v));
42840             case "b":
42841                 return (v == "1");
42842             case "a":
42843                 var all = [];
42844                 if(v != ''){
42845                     Ext.each(v.split('^'), function(val){
42846                         all.push(this.decodeValue(val));
42847                     }, this);
42848                 }
42849                 return all;
42850            case "o":
42851                 var all = {};
42852                 if(v != ''){
42853                     Ext.each(v.split('^'), function(val){
42854                         var kv = val.split('=');
42855                         all[kv[0]] = this.decodeValue(kv[1]);
42856                     }, this);
42857                 }
42858                 return all;
42859            default:
42860                 return v;
42861         }
42862     },
42863
42864     /**
42865      * Encodes a value including type information.  Decode with {@link #decodeValue}.
42866      * @param {Mixed} value The value to encode
42867      * @return {String} The encoded value
42868      */
42869     encodeValue : function(v){
42870         var enc;
42871         if(typeof v == "number"){
42872             enc = "n:" + v;
42873         }else if(typeof v == "boolean"){
42874             enc = "b:" + (v ? "1" : "0");
42875         }else if(Ext.isDate(v)){
42876             enc = "d:" + v.toGMTString();
42877         }else if(Ext.isArray(v)){
42878             var flat = "";
42879             for(var i = 0, len = v.length; i < len; i++){
42880                 flat += this.encodeValue(v[i]);
42881                 if(i != len-1) flat += "^";
42882             }
42883             enc = "a:" + flat;
42884         }else if(typeof v == "object"){
42885             var flat = "";
42886             for(var key in v){
42887                 if(typeof v[key] != "function" && v[key] !== undefined){
42888                     flat += key + "=" + this.encodeValue(v[key]) + "^";
42889                 }
42890             }
42891             enc = "o:" + flat.substring(0, flat.length-1);
42892         }else{
42893             enc = "s:" + v;
42894         }
42895         return escape(enc);
42896     }
42897 });
42898 /**
42899  * @class Ext.state.Manager
42900  * This is the global state manager. By default all components that are "state aware" check this class
42901  * for state information if you don't pass them a custom state provider. In order for this class
42902  * to be useful, it must be initialized with a provider when your application initializes. Example usage:
42903  <pre><code>
42904 // in your initialization function
42905 init : function(){
42906    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
42907    var win = new Window(...);
42908    win.restoreState();
42909 }
42910  </code></pre>
42911  * @singleton
42912  */
42913 Ext.state.Manager = function(){
42914     var provider = new Ext.state.Provider();
42915
42916     return {
42917         /**
42918          * Configures the default state provider for your application
42919          * @param {Provider} stateProvider The state provider to set
42920          */
42921         setProvider : function(stateProvider){
42922             provider = stateProvider;
42923         },
42924
42925         /**
42926          * Returns the current value for a key
42927          * @param {String} name The key name
42928          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
42929          * @return {Mixed} The state data
42930          */
42931         get : function(key, defaultValue){
42932             return provider.get(key, defaultValue);
42933         },
42934
42935         /**
42936          * Sets the value for a key
42937          * @param {String} name The key name
42938          * @param {Mixed} value The state data
42939          */
42940          set : function(key, value){
42941             provider.set(key, value);
42942         },
42943
42944         /**
42945          * Clears a value from the state
42946          * @param {String} name The key name
42947          */
42948         clear : function(key){
42949             provider.clear(key);
42950         },
42951
42952         /**
42953          * Gets the currently configured state provider
42954          * @return {Provider} The state provider
42955          */
42956         getProvider : function(){
42957             return provider;
42958         }
42959     };
42960 }();
42961 /**
42962  * @class Ext.state.CookieProvider
42963  * @extends Ext.state.Provider
42964  * The default Provider implementation which saves state via cookies.
42965  * <br />Usage:
42966  <pre><code>
42967    var cp = new Ext.state.CookieProvider({
42968        path: "/cgi-bin/",
42969        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days
42970        domain: "extjs.com"
42971    });
42972    Ext.state.Manager.setProvider(cp);
42973  </code></pre>
42974  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
42975  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
42976  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
42977  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include
42978  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
42979  * domain the page is running on including the 'www' like 'www.extjs.com')
42980  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
42981  * @constructor
42982  * Create a new CookieProvider
42983  * @param {Object} config The configuration object
42984  */
42985 Ext.state.CookieProvider = function(config){
42986     Ext.state.CookieProvider.superclass.constructor.call(this);
42987     this.path = "/";
42988     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
42989     this.domain = null;
42990     this.secure = false;
42991     Ext.apply(this, config);
42992     this.state = this.readCookies();
42993 };
42994
42995 Ext.extend(Ext.state.CookieProvider, Ext.state.Provider, {
42996     // private
42997     set : function(name, value){
42998         if(typeof value == "undefined" || value === null){
42999             this.clear(name);
43000             return;
43001         }
43002         this.setCookie(name, value);
43003         Ext.state.CookieProvider.superclass.set.call(this, name, value);
43004     },
43005
43006     // private
43007     clear : function(name){
43008         this.clearCookie(name);
43009         Ext.state.CookieProvider.superclass.clear.call(this, name);
43010     },
43011
43012     // private
43013     readCookies : function(){
43014         var cookies = {};
43015         var c = document.cookie + ";";
43016         var re = /\s?(.*?)=(.*?);/g;
43017         var matches;
43018         while((matches = re.exec(c)) != null){
43019             var name = matches[1];
43020             var value = matches[2];
43021             if(name && name.substring(0,3) == "ys-"){
43022                 cookies[name.substr(3)] = this.decodeValue(value);
43023             }
43024         }
43025         return cookies;
43026     },
43027
43028     // private
43029     setCookie : function(name, value){
43030         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
43031            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
43032            ((this.path == null) ? "" : ("; path=" + this.path)) +
43033            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
43034            ((this.secure == true) ? "; secure" : "");
43035     },
43036
43037     // private
43038     clearCookie : function(name){
43039         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
43040            ((this.path == null) ? "" : ("; path=" + this.path)) +
43041            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
43042            ((this.secure == true) ? "; secure" : "");
43043     }
43044 });/**
43045  * @class Ext.DataView
43046  * @extends Ext.BoxComponent
43047  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
43048  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
43049  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
43050  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
43051  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
43052  * config must be provided for the DataView to determine what nodes it will be working with.</b>
43053  *
43054  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
43055  * <pre><code>
43056 var store = new Ext.data.JsonStore({
43057     url: 'get-images.php',
43058     root: 'images',
43059     fields: [
43060         'name', 'url',
43061         {name:'size', type: 'float'},
43062         {name:'lastmod', type:'date', dateFormat:'timestamp'}
43063     ]
43064 });
43065 store.load();
43066
43067 var tpl = new Ext.XTemplate(
43068     '&lt;tpl for="."&gt;',
43069         '&lt;div class="thumb-wrap" id="{name}"&gt;',
43070         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
43071         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
43072     '&lt;/tpl&gt;',
43073     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
43074 );
43075
43076 var panel = new Ext.Panel({
43077     id:'images-view',
43078     frame:true,
43079     width:535,
43080     autoHeight:true,
43081     collapsible:true,
43082     layout:'fit',
43083     title:'Simple DataView',
43084
43085     items: new Ext.DataView({
43086         store: store,
43087         tpl: tpl,
43088         autoHeight:true,
43089         multiSelect: true,
43090         overClass:'x-view-over',
43091         itemSelector:'div.thumb-wrap',
43092         emptyText: 'No images to display'
43093     })
43094 });
43095 panel.render(document.body);
43096 </code></pre>
43097  * @constructor
43098  * Create a new DataView
43099  * @param {Object} config The config object
43100  * @xtype dataview
43101  */
43102 Ext.DataView = Ext.extend(Ext.BoxComponent, {
43103     /**
43104      * @cfg {String/Array} tpl
43105      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
43106      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
43107      */
43108     /**
43109      * @cfg {Ext.data.Store} store
43110      * The {@link Ext.data.Store} to bind this DataView to.
43111      */
43112     /**
43113      * @cfg {String} itemSelector
43114      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
43115      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
43116      * working with.
43117      */
43118     /**
43119      * @cfg {Boolean} multiSelect
43120      * True to allow selection of more than one item at a time, false to allow selection of only a single item
43121      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
43122      */
43123     /**
43124      * @cfg {Boolean} singleSelect
43125      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
43126      * Note that if {@link #multiSelect} = true, this value will be ignored.
43127      */
43128     /**
43129      * @cfg {Boolean} simpleSelect
43130      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
43131      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
43132      */
43133     /**
43134      * @cfg {String} overClass
43135      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
43136      */
43137     /**
43138      * @cfg {String} loadingText
43139      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
43140      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
43141      * contents will continue to display normally until the new data is loaded and the contents are replaced.
43142      */
43143     /**
43144      * @cfg {String} selectedClass
43145      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
43146      */
43147     selectedClass : "x-view-selected",
43148     /**
43149      * @cfg {String} emptyText
43150      * The text to display in the view when there is no data to display (defaults to '').
43151      */
43152     emptyText : "",
43153
43154     /**
43155      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
43156      */
43157     deferEmptyText: true,
43158     /**
43159      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
43160      */
43161     trackOver: false,
43162     
43163     /**
43164      * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
43165      * you wish to provide custom transition animations via a plugin (defaults to false)
43166      */
43167     blockRefresh: false,
43168
43169     //private
43170     last: false,
43171
43172     // private
43173     initComponent : function(){
43174         Ext.DataView.superclass.initComponent.call(this);
43175         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
43176             this.tpl = new Ext.XTemplate(this.tpl);
43177         }
43178
43179         this.addEvents(
43180             /**
43181              * @event beforeclick
43182              * Fires before a click is processed. Returns false to cancel the default action.
43183              * @param {Ext.DataView} this
43184              * @param {Number} index The index of the target node
43185              * @param {HTMLElement} node The target node
43186              * @param {Ext.EventObject} e The raw event object
43187              */
43188             "beforeclick",
43189             /**
43190              * @event click
43191              * Fires when a template node is clicked.
43192              * @param {Ext.DataView} this
43193              * @param {Number} index The index of the target node
43194              * @param {HTMLElement} node The target node
43195              * @param {Ext.EventObject} e The raw event object
43196              */
43197             "click",
43198             /**
43199              * @event mouseenter
43200              * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.
43201              * @param {Ext.DataView} this
43202              * @param {Number} index The index of the target node
43203              * @param {HTMLElement} node The target node
43204              * @param {Ext.EventObject} e The raw event object
43205              */
43206             "mouseenter",
43207             /**
43208              * @event mouseleave
43209              * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.
43210              * @param {Ext.DataView} this
43211              * @param {Number} index The index of the target node
43212              * @param {HTMLElement} node The target node
43213              * @param {Ext.EventObject} e The raw event object
43214              */
43215             "mouseleave",
43216             /**
43217              * @event containerclick
43218              * Fires when a click occurs and it is not on a template node.
43219              * @param {Ext.DataView} this
43220              * @param {Ext.EventObject} e The raw event object
43221              */
43222             "containerclick",
43223             /**
43224              * @event dblclick
43225              * Fires when a template node is double clicked.
43226              * @param {Ext.DataView} this
43227              * @param {Number} index The index of the target node
43228              * @param {HTMLElement} node The target node
43229              * @param {Ext.EventObject} e The raw event object
43230              */
43231             "dblclick",
43232             /**
43233              * @event contextmenu
43234              * Fires when a template node is right clicked.
43235              * @param {Ext.DataView} this
43236              * @param {Number} index The index of the target node
43237              * @param {HTMLElement} node The target node
43238              * @param {Ext.EventObject} e The raw event object
43239              */
43240             "contextmenu",
43241             /**
43242              * @event containercontextmenu
43243              * Fires when a right click occurs that is not on a template node.
43244              * @param {Ext.DataView} this
43245              * @param {Ext.EventObject} e The raw event object
43246              */
43247             "containercontextmenu",
43248             /**
43249              * @event selectionchange
43250              * Fires when the selected nodes change.
43251              * @param {Ext.DataView} this
43252              * @param {Array} selections Array of the selected nodes
43253              */
43254             "selectionchange",
43255
43256             /**
43257              * @event beforeselect
43258              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
43259              * @param {Ext.DataView} this
43260              * @param {HTMLElement} node The node to be selected
43261              * @param {Array} selections Array of currently selected nodes
43262              */
43263             "beforeselect"
43264         );
43265
43266         this.store = Ext.StoreMgr.lookup(this.store);
43267         this.all = new Ext.CompositeElementLite();
43268         this.selected = new Ext.CompositeElementLite();
43269     },
43270
43271     // private
43272     afterRender : function(){
43273         Ext.DataView.superclass.afterRender.call(this);
43274
43275                 this.mon(this.getTemplateTarget(), {
43276             "click": this.onClick,
43277             "dblclick": this.onDblClick,
43278             "contextmenu": this.onContextMenu,
43279             scope:this
43280         });
43281
43282         if(this.overClass || this.trackOver){
43283             this.mon(this.getTemplateTarget(), {
43284                 "mouseover": this.onMouseOver,
43285                 "mouseout": this.onMouseOut,
43286                 scope:this
43287             });
43288         }
43289
43290         if(this.store){
43291             this.bindStore(this.store, true);
43292         }
43293     },
43294
43295     /**
43296      * Refreshes the view by reloading the data from the store and re-rendering the template.
43297      */
43298     refresh : function() {
43299         this.clearSelections(false, true);
43300         var el = this.getTemplateTarget();
43301         el.update("");
43302         var records = this.store.getRange();
43303         if(records.length < 1){
43304             if(!this.deferEmptyText || this.hasSkippedEmptyText){
43305                 el.update(this.emptyText);
43306             }
43307             this.all.clear();
43308         }else{
43309             this.tpl.overwrite(el, this.collectData(records, 0));
43310             this.all.fill(Ext.query(this.itemSelector, el.dom));
43311             this.updateIndexes(0);
43312         }
43313         this.hasSkippedEmptyText = true;
43314     },
43315
43316     getTemplateTarget: function(){
43317         return this.el;
43318     },
43319
43320     /**
43321      * Function which can be overridden to provide custom formatting for each Record that is used by this
43322      * DataView's {@link #tpl template} to render each node.
43323      * @param {Array/Object} data The raw data object that was used to create the Record.
43324      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
43325      * @param {Record} record The Record being prepared for rendering.
43326      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
43327      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
43328      */
43329     prepareData : function(data){
43330         return data;
43331     },
43332
43333     /**
43334      * <p>Function which can be overridden which returns the data object passed to this
43335      * DataView's {@link #tpl template} to render the whole DataView.</p>
43336      * <p>This is usually an Array of data objects, each element of which is processed by an
43337      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
43338      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
43339      * provide non-repeating data such as headings, totals etc.</p>
43340      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
43341      * @param {Number} startIndex the index number of the Record being prepared for rendering.
43342      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
43343      * contain <i>named</i> properties.
43344      */
43345     collectData : function(records, startIndex){
43346         var r = [];
43347         for(var i = 0, len = records.length; i < len; i++){
43348             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);
43349         }
43350         return r;
43351     },
43352
43353     // private
43354     bufferRender : function(records){
43355         var div = document.createElement('div');
43356         this.tpl.overwrite(div, this.collectData(records));
43357         return Ext.query(this.itemSelector, div);
43358     },
43359
43360     // private
43361     onUpdate : function(ds, record){
43362         var index = this.store.indexOf(record);
43363         if(index > -1){
43364             var sel = this.isSelected(index);
43365             var original = this.all.elements[index];
43366             var node = this.bufferRender([record], index)[0];
43367
43368             this.all.replaceElement(index, node, true);
43369             if(sel){
43370                 this.selected.replaceElement(original, node);
43371                 this.all.item(index).addClass(this.selectedClass);
43372             }
43373             this.updateIndexes(index, index);
43374         }
43375     },
43376
43377     // private
43378     onAdd : function(ds, records, index){
43379         if(this.all.getCount() === 0){
43380             this.refresh();
43381             return;
43382         }
43383         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
43384         if(index < this.all.getCount()){
43385             n = this.all.item(index).insertSibling(nodes, 'before', true);
43386             a.splice.apply(a, [index, 0].concat(nodes));
43387         }else{
43388             n = this.all.last().insertSibling(nodes, 'after', true);
43389             a.push.apply(a, nodes);
43390         }
43391         this.updateIndexes(index);
43392     },
43393
43394     // private
43395     onRemove : function(ds, record, index){
43396         this.deselect(index);
43397         this.all.removeElement(index, true);
43398         this.updateIndexes(index);
43399         if (this.store.getCount() === 0){
43400             this.refresh();
43401         }
43402     },
43403
43404     /**
43405      * Refreshes an individual node's data from the store.
43406      * @param {Number} index The item's data index in the store
43407      */
43408     refreshNode : function(index){
43409         this.onUpdate(this.store, this.store.getAt(index));
43410     },
43411
43412     // private
43413     updateIndexes : function(startIndex, endIndex){
43414         var ns = this.all.elements;
43415         startIndex = startIndex || 0;
43416         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
43417         for(var i = startIndex; i <= endIndex; i++){
43418             ns[i].viewIndex = i;
43419         }
43420     },
43421     
43422     /**
43423      * Returns the store associated with this DataView.
43424      * @return {Ext.data.Store} The store
43425      */
43426     getStore : function(){
43427         return this.store;
43428     },
43429
43430     /**
43431      * Changes the data store bound to this view and refreshes it.
43432      * @param {Store} store The store to bind to this view
43433      */
43434     bindStore : function(store, initial){
43435         if(!initial && this.store){
43436             if(store !== this.store && this.store.autoDestroy){
43437                 this.store.destroy();
43438             }else{
43439                 this.store.un("beforeload", this.onBeforeLoad, this);
43440                 this.store.un("datachanged", this.onDataChanged, this);
43441                 this.store.un("add", this.onAdd, this);
43442                 this.store.un("remove", this.onRemove, this);
43443                 this.store.un("update", this.onUpdate, this);
43444                 this.store.un("clear", this.refresh, this);
43445             }
43446             if(!store){
43447                 this.store = null;
43448             }
43449         }
43450         if(store){
43451             store = Ext.StoreMgr.lookup(store);
43452             store.on({
43453                 scope: this,
43454                 beforeload: this.onBeforeLoad,
43455                 datachanged: this.onDataChanged,
43456                 add: this.onAdd,
43457                 remove: this.onRemove,
43458                 update: this.onUpdate,
43459                 clear: this.refresh
43460             });
43461         }
43462         this.store = store;
43463         if(store){
43464             this.refresh();
43465         }
43466     },
43467     
43468     /**
43469      * @private
43470      * Calls this.refresh if this.blockRefresh is not true
43471      */
43472     onDataChanged: function() {
43473         if (this.blockRefresh !== true) {
43474             this.refresh.apply(this, arguments);
43475         }
43476     },
43477
43478     /**
43479      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
43480      * @param {HTMLElement} node
43481      * @return {HTMLElement} The template node
43482      */
43483     findItemFromChild : function(node){
43484         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
43485     },
43486
43487     // private
43488     onClick : function(e){
43489         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43490         if(item){
43491             var index = this.indexOf(item);
43492             if(this.onItemClick(item, index, e) !== false){
43493                 this.fireEvent("click", this, index, item, e);
43494             }
43495         }else{
43496             if(this.fireEvent("containerclick", this, e) !== false){
43497                 this.onContainerClick(e);
43498             }
43499         }
43500     },
43501
43502     onContainerClick : function(e){
43503         this.clearSelections();
43504     },
43505
43506     // private
43507     onContextMenu : function(e){
43508         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43509         if(item){
43510             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
43511         }else{
43512             this.fireEvent("containercontextmenu", this, e);
43513         }
43514     },
43515
43516     // private
43517     onDblClick : function(e){
43518         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43519         if(item){
43520             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
43521         }
43522     },
43523
43524     // private
43525     onMouseOver : function(e){
43526         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43527         if(item && item !== this.lastItem){
43528             this.lastItem = item;
43529             Ext.fly(item).addClass(this.overClass);
43530             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
43531         }
43532     },
43533
43534     // private
43535     onMouseOut : function(e){
43536         if(this.lastItem){
43537             if(!e.within(this.lastItem, true, true)){
43538                 Ext.fly(this.lastItem).removeClass(this.overClass);
43539                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
43540                 delete this.lastItem;
43541             }
43542         }
43543     },
43544
43545     // private
43546     onItemClick : function(item, index, e){
43547         if(this.fireEvent("beforeclick", this, index, item, e) === false){
43548             return false;
43549         }
43550         if(this.multiSelect){
43551             this.doMultiSelection(item, index, e);
43552             e.preventDefault();
43553         }else if(this.singleSelect){
43554             this.doSingleSelection(item, index, e);
43555             e.preventDefault();
43556         }
43557         return true;
43558     },
43559
43560     // private
43561     doSingleSelection : function(item, index, e){
43562         if(e.ctrlKey && this.isSelected(index)){
43563             this.deselect(index);
43564         }else{
43565             this.select(index, false);
43566         }
43567     },
43568
43569     // private
43570     doMultiSelection : function(item, index, e){
43571         if(e.shiftKey && this.last !== false){
43572             var last = this.last;
43573             this.selectRange(last, index, e.ctrlKey);
43574             this.last = last; // reset the last
43575         }else{
43576             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
43577                 this.deselect(index);
43578             }else{
43579                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
43580             }
43581         }
43582     },
43583
43584     /**
43585      * Gets the number of selected nodes.
43586      * @return {Number} The node count
43587      */
43588     getSelectionCount : function(){
43589         return this.selected.getCount();
43590     },
43591
43592     /**
43593      * Gets the currently selected nodes.
43594      * @return {Array} An array of HTMLElements
43595      */
43596     getSelectedNodes : function(){
43597         return this.selected.elements;
43598     },
43599
43600     /**
43601      * Gets the indexes of the selected nodes.
43602      * @return {Array} An array of numeric indexes
43603      */
43604     getSelectedIndexes : function(){
43605         var indexes = [], s = this.selected.elements;
43606         for(var i = 0, len = s.length; i < len; i++){
43607             indexes.push(s[i].viewIndex);
43608         }
43609         return indexes;
43610     },
43611
43612     /**
43613      * Gets an array of the selected records
43614      * @return {Array} An array of {@link Ext.data.Record} objects
43615      */
43616     getSelectedRecords : function(){
43617         var r = [], s = this.selected.elements;
43618         for(var i = 0, len = s.length; i < len; i++){
43619             r[r.length] = this.store.getAt(s[i].viewIndex);
43620         }
43621         return r;
43622     },
43623
43624     /**
43625      * Gets an array of the records from an array of nodes
43626      * @param {Array} nodes The nodes to evaluate
43627      * @return {Array} records The {@link Ext.data.Record} objects
43628      */
43629     getRecords : function(nodes){
43630         var r = [], s = nodes;
43631         for(var i = 0, len = s.length; i < len; i++){
43632             r[r.length] = this.store.getAt(s[i].viewIndex);
43633         }
43634         return r;
43635     },
43636
43637     /**
43638      * Gets a record from a node
43639      * @param {HTMLElement} node The node to evaluate
43640      * @return {Record} record The {@link Ext.data.Record} object
43641      */
43642     getRecord : function(node){
43643         return this.store.getAt(node.viewIndex);
43644     },
43645
43646     /**
43647      * Clears all selections.
43648      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
43649      */
43650     clearSelections : function(suppressEvent, skipUpdate){
43651         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
43652             if(!skipUpdate){
43653                 this.selected.removeClass(this.selectedClass);
43654             }
43655             this.selected.clear();
43656             this.last = false;
43657             if(!suppressEvent){
43658                 this.fireEvent("selectionchange", this, this.selected.elements);
43659             }
43660         }
43661     },
43662
43663     /**
43664      * Returns true if the passed node is selected, else false.
43665      * @param {HTMLElement/Number/Ext.data.Record} node The node, node index or record to check
43666      * @return {Boolean} True if selected, else false
43667      */
43668     isSelected : function(node){
43669         return this.selected.contains(this.getNode(node));
43670     },
43671
43672     /**
43673      * Deselects a node.
43674      * @param {HTMLElement/Number/Record} node The node, node index or record to deselect
43675      */
43676     deselect : function(node){
43677         if(this.isSelected(node)){
43678             node = this.getNode(node);
43679             this.selected.removeElement(node);
43680             if(this.last == node.viewIndex){
43681                 this.last = false;
43682             }
43683             Ext.fly(node).removeClass(this.selectedClass);
43684             this.fireEvent("selectionchange", this, this.selected.elements);
43685         }
43686     },
43687
43688     /**
43689      * Selects a set of nodes.
43690      * @param {Array/HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node,
43691      * id of a template node, record associated with a node or an array of any of those to select
43692      * @param {Boolean} keepExisting (optional) true to keep existing selections
43693      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
43694      */
43695     select : function(nodeInfo, keepExisting, suppressEvent){
43696         if(Ext.isArray(nodeInfo)){
43697             if(!keepExisting){
43698                 this.clearSelections(true);
43699             }
43700             for(var i = 0, len = nodeInfo.length; i < len; i++){
43701                 this.select(nodeInfo[i], true, true);
43702             }
43703             if(!suppressEvent){
43704                 this.fireEvent("selectionchange", this, this.selected.elements);
43705             }
43706         } else{
43707             var node = this.getNode(nodeInfo);
43708             if(!keepExisting){
43709                 this.clearSelections(true);
43710             }
43711             if(node && !this.isSelected(node)){
43712                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
43713                     Ext.fly(node).addClass(this.selectedClass);
43714                     this.selected.add(node);
43715                     this.last = node.viewIndex;
43716                     if(!suppressEvent){
43717                         this.fireEvent("selectionchange", this, this.selected.elements);
43718                     }
43719                 }
43720             }
43721         }
43722     },
43723
43724     /**
43725      * Selects a range of nodes. All nodes between start and end are selected.
43726      * @param {Number} start The index of the first node in the range
43727      * @param {Number} end The index of the last node in the range
43728      * @param {Boolean} keepExisting (optional) True to retain existing selections
43729      */
43730     selectRange : function(start, end, keepExisting){
43731         if(!keepExisting){
43732             this.clearSelections(true);
43733         }
43734         this.select(this.getNodes(start, end), true);
43735     },
43736
43737     /**
43738      * Gets a template node.
43739      * @param {HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node, 
43740      * the id of a template node or the record associated with the node.
43741      * @return {HTMLElement} The node or null if it wasn't found
43742      */
43743     getNode : function(nodeInfo){
43744         if(Ext.isString(nodeInfo)){
43745             return document.getElementById(nodeInfo);
43746         }else if(Ext.isNumber(nodeInfo)){
43747             return this.all.elements[nodeInfo];
43748         }else if(nodeInfo instanceof Ext.data.Record){
43749             var idx = this.store.indexOf(nodeInfo);
43750             return this.all.elements[idx];
43751         }
43752         return nodeInfo;
43753     },
43754
43755     /**
43756      * Gets a range nodes.
43757      * @param {Number} start (optional) The index of the first node in the range
43758      * @param {Number} end (optional) The index of the last node in the range
43759      * @return {Array} An array of nodes
43760      */
43761     getNodes : function(start, end){
43762         var ns = this.all.elements;
43763         start = start || 0;
43764         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
43765         var nodes = [], i;
43766         if(start <= end){
43767             for(i = start; i <= end && ns[i]; i++){
43768                 nodes.push(ns[i]);
43769             }
43770         } else{
43771             for(i = start; i >= end && ns[i]; i--){
43772                 nodes.push(ns[i]);
43773             }
43774         }
43775         return nodes;
43776     },
43777
43778     /**
43779      * Finds the index of the passed node.
43780      * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
43781      * or a record associated with a node.
43782      * @return {Number} The index of the node or -1
43783      */
43784     indexOf : function(node){
43785         node = this.getNode(node);
43786         if(Ext.isNumber(node.viewIndex)){
43787             return node.viewIndex;
43788         }
43789         return this.all.indexOf(node);
43790     },
43791
43792     // private
43793     onBeforeLoad : function(){
43794         if(this.loadingText){
43795             this.clearSelections(false, true);
43796             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
43797             this.all.clear();
43798         }
43799     },
43800
43801     onDestroy : function(){
43802         this.all.clear();
43803         this.selected.clear();
43804         Ext.DataView.superclass.onDestroy.call(this);
43805         this.bindStore(null);
43806     }
43807 });
43808
43809 /**
43810  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
43811  * @param {Store} store The store to bind to this view
43812  */
43813 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
43814
43815 Ext.reg('dataview', Ext.DataView);
43816 /**
43817  * @class Ext.list.ListView
43818  * @extends Ext.DataView
43819  * <p>Ext.list.ListView is a fast and light-weight implentation of a
43820  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>
43821  * <div class="mdetail-params"><ul>
43822  * <li>resizable columns</li>
43823  * <li>selectable</li>
43824  * <li>column widths are initially proportioned by percentage based on the container
43825  * width and number of columns</li>
43826  * <li>uses templates to render the data in any required format</li>
43827  * <li>no horizontal scrolling</li>
43828  * <li>no editing</li>
43829  * </ul></div>
43830  * <p>Example usage:</p>
43831  * <pre><code>
43832 // consume JSON of this form:
43833 {
43834    "images":[
43835       {
43836          "name":"dance_fever.jpg",
43837          "size":2067,
43838          "lastmod":1236974993000,
43839          "url":"images\/thumbs\/dance_fever.jpg"
43840       },
43841       {
43842          "name":"zack_sink.jpg",
43843          "size":2303,
43844          "lastmod":1236974993000,
43845          "url":"images\/thumbs\/zack_sink.jpg"
43846       }
43847    ]
43848 }
43849 var store = new Ext.data.JsonStore({
43850     url: 'get-images.php',
43851     root: 'images',
43852     fields: [
43853         'name', 'url',
43854         {name:'size', type: 'float'},
43855         {name:'lastmod', type:'date', dateFormat:'timestamp'}
43856     ]
43857 });
43858 store.load();
43859
43860 var listView = new Ext.list.ListView({
43861     store: store,
43862     multiSelect: true,
43863     emptyText: 'No images to display',
43864     reserveScrollOffset: true,
43865     columns: [{
43866         header: 'File',
43867         width: .5,
43868         dataIndex: 'name'
43869     },{
43870         header: 'Last Modified',
43871         width: .35,
43872         dataIndex: 'lastmod',
43873         tpl: '{lastmod:date("m-d h:i a")}'
43874     },{
43875         header: 'Size',
43876         dataIndex: 'size',
43877         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()
43878         align: 'right'
43879     }]
43880 });
43881
43882 // put it in a Panel so it looks pretty
43883 var panel = new Ext.Panel({
43884     id:'images-view',
43885     width:425,
43886     height:250,
43887     collapsible:true,
43888     layout:'fit',
43889     title:'Simple ListView <i>(0 items selected)</i>',
43890     items: listView
43891 });
43892 panel.render(document.body);
43893
43894 // little bit of feedback
43895 listView.on('selectionchange', function(view, nodes){
43896     var l = nodes.length;
43897     var s = l != 1 ? 's' : '';
43898     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');
43899 });
43900  * </code></pre>
43901  * @constructor
43902  * @param {Object} config
43903  * @xtype listview
43904  */
43905 Ext.list.ListView = Ext.extend(Ext.DataView, {
43906     /**
43907      * Set this property to <tt>true</tt> to disable the header click handler disabling sort
43908      * (defaults to <tt>false</tt>).
43909      * @type Boolean
43910      * @property disableHeaders
43911      */
43912     /**
43913      * @cfg {Boolean} hideHeaders
43914      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so
43915      * the {@link #internalTpl header row} will be shown).
43916      */
43917     /**
43918      * @cfg {String} itemSelector
43919      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.
43920      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)
43921      * that will be used to determine what nodes the ListView will be working with.
43922      */
43923     itemSelector: 'dl',
43924     /**
43925      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to
43926      * <tt>'x-list-selected'</tt>). An example overriding the default styling:
43927     <pre><code>
43928     .x-list-selected {background-color: yellow;}
43929     </code></pre>
43930      * @type String
43931      */
43932     selectedClass:'x-list-selected',
43933     /**
43934      * @cfg {String} overClass The CSS class applied when over a row (defaults to
43935      * <tt>'x-list-over'</tt>). An example overriding the default styling:
43936     <pre><code>
43937     .x-list-over {background-color: orange;}
43938     </code></pre>
43939      * @type String
43940      */
43941     overClass:'x-list-over',
43942     /**
43943      * @cfg {Boolean} reserveScrollOffset
43944      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>
43945      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured
43946      * <b><tt>{@link #scrollOffset}</tt></b> immediately.
43947      */
43948     /**
43949      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to
43950      * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
43951      * calculated.
43952      */
43953     scrollOffset : undefined,
43954     /**
43955      * @cfg {Boolean/Object} columnResize
43956      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer}
43957      * to enable the columns to be resizable (defaults to <tt>true</tt>).
43958      */
43959     columnResize: true,
43960     /**
43961      * @cfg {Array} columns An array of column configuration objects, for example:
43962      * <pre><code>
43963 {
43964     align: 'right',
43965     dataIndex: 'size',
43966     header: 'Size',
43967     tpl: '{size:fileSize}',
43968     width: .35
43969 }
43970      * </code></pre>
43971      * Acceptable properties for each column configuration object are:
43972      * <div class="mdetail-params"><ul>
43973      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property
43974      * of the column. Defaults to <tt>'left'</tt>.</div></li>
43975      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
43976      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>
43977      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
43978      * {@link Ext.grid.Column#header header} for details.</div></li>
43979      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the
43980      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
43981      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>
43982      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width
43983      * this column should be allocated.  Columns that have no width specified will be
43984      * allocated with an equal percentage to fill 100% of the container width.  To easily take
43985      * advantage of the full container width, leave the width of at least one column undefined.
43986      * Note that if you do not want to take up the full width of the container, the width of
43987      * every column needs to be explicitly defined.</div></li>
43988      * </ul></div>
43989      */
43990     /**
43991      * @cfg {Boolean/Object} columnSort
43992      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter}
43993      * to enable the columns to be sortable (defaults to <tt>true</tt>).
43994      */
43995     columnSort: true,
43996     /**
43997      * @cfg {String/Array} internalTpl
43998      * The template to be used for the header row.  See {@link #tpl} for more details.
43999      */
44000
44001     /*
44002      * IE has issues when setting percentage based widths to 100%. Default to 99.
44003      */
44004     maxWidth: Ext.isIE ? 99 : 100,
44005
44006     initComponent : function(){
44007         if(this.columnResize){
44008             this.colResizer = new Ext.list.ColumnResizer(this.colResizer);
44009             this.colResizer.init(this);
44010         }
44011         if(this.columnSort){
44012             this.colSorter = new Ext.list.Sorter(this.columnSort);
44013             this.colSorter.init(this);
44014         }
44015         if(!this.internalTpl){
44016             this.internalTpl = new Ext.XTemplate(
44017                 '<div class="x-list-header"><div class="x-list-header-inner">',
44018                     '<tpl for="columns">',
44019                     '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',
44020                         '{header}',
44021                     '</em></div>',
44022                     '</tpl>',
44023                     '<div class="x-clear"></div>',
44024                 '</div></div>',
44025                 '<div class="x-list-body"><div class="x-list-body-inner">',
44026                 '</div></div>'
44027             );
44028         }
44029         if(!this.tpl){
44030             this.tpl = new Ext.XTemplate(
44031                 '<tpl for="rows">',
44032                     '<dl>',
44033                         '<tpl for="parent.columns">',
44034                         '<dt style="width:{[values.width*100]}%;text-align:{align};">',
44035                         '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">',
44036                             '{[values.tpl.apply(parent)]}',
44037                         '</em></dt>',
44038                         '</tpl>',
44039                         '<div class="x-clear"></div>',
44040                     '</dl>',
44041                 '</tpl>'
44042             );
44043         };
44044
44045         var cs = this.columns,
44046             allocatedWidth = 0,
44047             colsWithWidth = 0,
44048             len = cs.length,
44049             columns = [];
44050
44051         for(var i = 0; i < len; i++){
44052             var c = cs[i];
44053             if(!c.isColumn) {
44054                 c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn';
44055                 c = Ext.create(c);
44056             }
44057             if(c.width) {
44058                 allocatedWidth += c.width*100;
44059                 colsWithWidth++;
44060             }
44061             columns.push(c);
44062         }
44063
44064         cs = this.columns = columns;
44065
44066         // auto calculate missing column widths
44067         if(colsWithWidth < len){
44068             var remaining = len - colsWithWidth;
44069             if(allocatedWidth < this.maxWidth){
44070                 var perCol = ((this.maxWidth-allocatedWidth) / remaining)/100;
44071                 for(var j = 0; j < len; j++){
44072                     var c = cs[j];
44073                     if(!c.width){
44074                         c.width = perCol;
44075                     }
44076                 }
44077             }
44078         }
44079         Ext.list.ListView.superclass.initComponent.call(this);
44080     },
44081
44082     onRender : function(){
44083         this.autoEl = {
44084             cls: 'x-list-wrap'
44085         };
44086         Ext.list.ListView.superclass.onRender.apply(this, arguments);
44087
44088         this.internalTpl.overwrite(this.el, {columns: this.columns});
44089
44090         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);
44091         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);
44092
44093         if(this.hideHeaders){
44094             this.el.dom.firstChild.style.display = 'none';
44095         }
44096     },
44097
44098     getTemplateTarget : function(){
44099         return this.innerBody;
44100     },
44101
44102     /**
44103      * <p>Function which can be overridden which returns the data object passed to this
44104      * view's {@link #tpl template} to render the whole ListView. The returned object
44105      * shall contain the following properties:</p>
44106      * <div class="mdetail-params"><ul>
44107      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>
44108      * <li><b>rows</b> : String<div class="sub-desc">See
44109      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>
44110      * </ul></div>
44111      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
44112      * @param {Number} startIndex the index number of the Record being prepared for rendering.
44113      * @return {Object} A data object containing properties to be processed by a repeating
44114      * XTemplate as described above.
44115      */
44116     collectData : function(){
44117         var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments);
44118         return {
44119             columns: this.columns,
44120             rows: rs
44121         }
44122     },
44123
44124     verifyInternalSize : function(){
44125         if(this.lastSize){
44126             this.onResize(this.lastSize.width, this.lastSize.height);
44127         }
44128     },
44129
44130     // private
44131     onResize : function(w, h){
44132         var bd = this.innerBody.dom;
44133         var hd = this.innerHd.dom;
44134         if(!bd){
44135             return;
44136         }
44137         var bdp = bd.parentNode;
44138         if(Ext.isNumber(w)){
44139             var sw = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
44140             if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){
44141                 bd.style.width = sw + 'px';
44142                 hd.style.width = sw + 'px';
44143             }else{
44144                 bd.style.width = w + 'px';
44145                 hd.style.width = w + 'px';
44146                 setTimeout(function(){
44147                     if((bdp.offsetWidth - bdp.clientWidth) > 10){
44148                         bd.style.width = sw + 'px';
44149                         hd.style.width = sw + 'px';
44150                     }
44151                 }, 10);
44152             }
44153         }
44154         if(Ext.isNumber(h)){
44155             bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';
44156         }
44157     },
44158
44159     updateIndexes : function(){
44160         Ext.list.ListView.superclass.updateIndexes.apply(this, arguments);
44161         this.verifyInternalSize();
44162     },
44163
44164     findHeaderIndex : function(hd){
44165         hd = hd.dom || hd;
44166         var pn = hd.parentNode, cs = pn.parentNode.childNodes;
44167         for(var i = 0, c; c = cs[i]; i++){
44168             if(c == pn){
44169                 return i;
44170             }
44171         }
44172         return -1;
44173     },
44174
44175     setHdWidths : function(){
44176         var els = this.innerHd.dom.getElementsByTagName('div');
44177         for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){
44178             els[i].style.width = (cs[i].width*100) + '%';
44179         }
44180     }
44181 });
44182
44183 Ext.reg('listview', Ext.list.ListView);
44184
44185 // Backwards compatibility alias
44186 Ext.ListView = Ext.list.ListView;/**
44187  * @class Ext.list.Column
44188  * <p>This class encapsulates column configuration data to be used in the initialization of a
44189  * {@link Ext.list.ListView ListView}.</p>
44190  * <p>While subclasses are provided to render data in different ways, this class renders a passed
44191  * data field unchanged and is usually used for textual columns.</p>
44192  */
44193 Ext.list.Column = Ext.extend(Object, {
44194     /**
44195      * @private
44196      * @cfg {Boolean} isColumn
44197      * Used by ListView constructor method to avoid reprocessing a Column
44198      * if <code>isColumn</code> is not set ListView will recreate a new Ext.list.Column
44199      * Defaults to true.
44200      */
44201     isColumn: true,
44202     
44203     /**
44204      * @cfg {String} align
44205      * Set the CSS text-align property of the column. Defaults to <tt>'left'</tt>.
44206      */        
44207     align: 'left',
44208     /**
44209      * @cfg {String} header Optional. The header text to be used as innerHTML
44210      * (html tags are accepted) to display in the ListView.  <b>Note</b>: to
44211      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
44212      */    
44213     header: '',
44214     
44215     /**
44216      * @cfg {Number} width Optional. Percentage of the container width
44217      * this column should be allocated.  Columns that have no width specified will be
44218      * allocated with an equal percentage to fill 100% of the container width.  To easily take
44219      * advantage of the full container width, leave the width of at least one column undefined.
44220      * Note that if you do not want to take up the full width of the container, the width of
44221      * every column needs to be explicitly defined.
44222      */    
44223     width: null,
44224
44225     /**
44226      * @cfg {String} cls Optional. This option can be used to add a CSS class to the cell of each
44227      * row for this column.
44228      */
44229     cls: '',
44230     
44231     /**
44232      * @cfg {String} tpl Optional. Specify a string to pass as the
44233      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
44234      * will be implicitly created using the <tt>dataIndex</tt>.
44235      */
44236
44237     /**
44238      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
44239      * ListViews's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
44240      * which to draw the column's value.</p>
44241      */
44242     
44243     constructor : function(c){
44244         if(!c.tpl){
44245             c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');
44246         }
44247         else if(Ext.isString(c.tpl)){
44248             c.tpl = new Ext.XTemplate(c.tpl);
44249         }
44250         
44251         Ext.apply(this, c);
44252     }
44253 });
44254
44255 Ext.reg('lvcolumn', Ext.list.Column);
44256
44257 /**
44258  * @class Ext.list.NumberColumn
44259  * @extends Ext.list.Column
44260  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
44261  * {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column} for more details.</p>
44262  */
44263 Ext.list.NumberColumn = Ext.extend(Ext.list.Column, {
44264     /**
44265      * @cfg {String} format
44266      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
44267      * (defaults to <tt>'0,000.00'</tt>).
44268      */    
44269     format: '0,000.00',
44270     
44271     constructor : function(c) {
44272         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':number("' + (c.format || this.format) + '")}');       
44273         Ext.list.NumberColumn.superclass.constructor.call(this, c);
44274     }
44275 });
44276
44277 Ext.reg('lvnumbercolumn', Ext.list.NumberColumn);
44278
44279 /**
44280  * @class Ext.list.DateColumn
44281  * @extends Ext.list.Column
44282  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
44283  * {@link #format}. See the {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column}
44284  * for more details.</p>
44285  */
44286 Ext.list.DateColumn = Ext.extend(Ext.list.Column, {
44287     format: 'm/d/Y',
44288     constructor : function(c) {
44289         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':date("' + (c.format || this.format) + '")}');      
44290         Ext.list.DateColumn.superclass.constructor.call(this, c);
44291     }
44292 });
44293 Ext.reg('lvdatecolumn', Ext.list.DateColumn);
44294
44295 /**
44296  * @class Ext.list.BooleanColumn
44297  * @extends Ext.list.Column
44298  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.list.Column#xtype xtype}
44299  * config option of {@link Ext.list.Column} for more details.</p>
44300  */
44301 Ext.list.BooleanColumn = Ext.extend(Ext.list.Column, {
44302     /**
44303      * @cfg {String} trueText
44304      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
44305      */
44306     trueText: 'true',
44307     /**
44308      * @cfg {String} falseText
44309      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
44310      * <tt>'false'</tt>).
44311      */
44312     falseText: 'false',
44313     /**
44314      * @cfg {String} undefinedText
44315      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
44316      */
44317     undefinedText: '&#160;',
44318     
44319     constructor : function(c) {
44320         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}');
44321         
44322         var t = this.trueText, f = this.falseText, u = this.undefinedText;
44323         c.tpl.format = function(v){
44324             if(v === undefined){
44325                 return u;
44326             }
44327             if(!v || v === 'false'){
44328                 return f;
44329             }
44330             return t;
44331         };
44332         
44333         Ext.list.DateColumn.superclass.constructor.call(this, c);
44334     }
44335 });
44336
44337 Ext.reg('lvbooleancolumn', Ext.list.BooleanColumn);/**
44338  * @class Ext.list.ColumnResizer
44339  * @extends Ext.util.Observable
44340  * <p>Supporting Class for Ext.list.ListView</p>
44341  * @constructor
44342  * @param {Object} config
44343  */
44344 Ext.list.ColumnResizer = Ext.extend(Ext.util.Observable, {
44345     /**
44346      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)
44347      */
44348     minPct: .05,
44349
44350     constructor: function(config){
44351         Ext.apply(this, config);
44352         Ext.list.ColumnResizer.superclass.constructor.call(this);
44353     },
44354     init : function(listView){
44355         this.view = listView;
44356         listView.on('render', this.initEvents, this);
44357     },
44358
44359     initEvents : function(view){
44360         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);
44361         this.tracker = new Ext.dd.DragTracker({
44362             onBeforeStart: this.onBeforeStart.createDelegate(this),
44363             onStart: this.onStart.createDelegate(this),
44364             onDrag: this.onDrag.createDelegate(this),
44365             onEnd: this.onEnd.createDelegate(this),
44366             tolerance: 3,
44367             autoStart: 300
44368         });
44369         this.tracker.initEl(view.innerHd);
44370         view.on('beforedestroy', this.tracker.destroy, this.tracker);
44371     },
44372
44373     handleHdMove : function(e, t){
44374         var hw = 5,
44375             x = e.getPageX(),
44376             hd = e.getTarget('em', 3, true);
44377         if(hd){
44378             var r = hd.getRegion(),
44379                 ss = hd.dom.style,
44380                 pn = hd.dom.parentNode;
44381
44382             if(x - r.left <= hw && pn != pn.parentNode.firstChild){
44383                 this.activeHd = Ext.get(pn.previousSibling.firstChild);
44384                 ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
44385             } else if(r.right - x <= hw && pn != pn.parentNode.lastChild.previousSibling){
44386                 this.activeHd = hd;
44387                 ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
44388             } else{
44389                 delete this.activeHd;
44390                 ss.cursor = '';
44391             }
44392         }
44393     },
44394
44395     onBeforeStart : function(e){
44396         this.dragHd = this.activeHd;
44397         return !!this.dragHd;
44398     },
44399
44400     onStart: function(e){
44401         this.view.disableHeaders = true;
44402         this.proxy = this.view.el.createChild({cls:'x-list-resizer'});
44403         this.proxy.setHeight(this.view.el.getHeight());
44404
44405         var x = this.tracker.getXY()[0],
44406             w = this.view.innerHd.getWidth();
44407
44408         this.hdX = this.dragHd.getX();
44409         this.hdIndex = this.view.findHeaderIndex(this.dragHd);
44410
44411         this.proxy.setX(this.hdX);
44412         this.proxy.setWidth(x-this.hdX);
44413
44414         this.minWidth = w*this.minPct;
44415         this.maxWidth = w - (this.minWidth*(this.view.columns.length-1-this.hdIndex));
44416     },
44417
44418     onDrag: function(e){
44419         var cursorX = this.tracker.getXY()[0];
44420         this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));
44421     },
44422
44423     onEnd: function(e){
44424         /* calculate desired width by measuring proxy and then remove it */
44425         var nw = this.proxy.getWidth();
44426         this.proxy.remove();
44427
44428         var index = this.hdIndex,
44429             vw = this.view,
44430             cs = vw.columns,
44431             len = cs.length,
44432             w = this.view.innerHd.getWidth(),
44433             minPct = this.minPct * 100,
44434             pct = Math.ceil((nw * vw.maxWidth) / w),
44435             diff = (cs[index].width * 100) - pct,
44436             eachItem = Math.floor(diff / (len-1-index)),
44437             mod = diff - (eachItem * (len-1-index));
44438
44439         for(var i = index+1; i < len; i++){
44440             var cw = (cs[i].width * 100) + eachItem,
44441                 ncw = Math.max(minPct, cw);
44442             if(cw != ncw){
44443                 mod += cw - ncw;
44444             }
44445             cs[i].width = ncw / 100;
44446         }
44447         cs[index].width = pct / 100;
44448         cs[index+1].width += (mod / 100);
44449         delete this.dragHd;
44450         vw.setHdWidths();
44451         vw.refresh();
44452         setTimeout(function(){
44453             vw.disableHeaders = false;
44454         }, 100);
44455     }
44456 });
44457
44458 // Backwards compatibility alias
44459 Ext.ListView.ColumnResizer = Ext.list.ColumnResizer;/**
44460  * @class Ext.list.Sorter
44461  * @extends Ext.util.Observable
44462  * <p>Supporting Class for Ext.list.ListView</p>
44463  * @constructor
44464  * @param {Object} config
44465  */
44466 Ext.list.Sorter = Ext.extend(Ext.util.Observable, {
44467     /**
44468      * @cfg {Array} sortClasses
44469      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)
44470      */
44471     sortClasses : ["sort-asc", "sort-desc"],
44472
44473     constructor: function(config){
44474         Ext.apply(this, config);
44475         Ext.list.Sorter.superclass.constructor.call(this);
44476     },
44477
44478     init : function(listView){
44479         this.view = listView;
44480         listView.on('render', this.initEvents, this);
44481     },
44482
44483     initEvents : function(view){
44484         view.mon(view.innerHd, 'click', this.onHdClick, this);
44485         view.innerHd.setStyle('cursor', 'pointer');
44486         view.mon(view.store, 'datachanged', this.updateSortState, this);
44487         this.updateSortState.defer(10, this, [view.store]);
44488     },
44489
44490     updateSortState : function(store){
44491         var state = store.getSortState();
44492         if(!state){
44493             return;
44494         }
44495         this.sortState = state;
44496         var cs = this.view.columns, sortColumn = -1;
44497         for(var i = 0, len = cs.length; i < len; i++){
44498             if(cs[i].dataIndex == state.field){
44499                 sortColumn = i;
44500                 break;
44501             }
44502         }
44503         if(sortColumn != -1){
44504             var sortDir = state.direction;
44505             this.updateSortIcon(sortColumn, sortDir);
44506         }
44507     },
44508
44509     updateSortIcon : function(col, dir){
44510         var sc = this.sortClasses;
44511         var hds = this.view.innerHd.select('em').removeClass(sc);
44512         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
44513     },
44514
44515     onHdClick : function(e){
44516         var hd = e.getTarget('em', 3);
44517         if(hd && !this.view.disableHeaders){
44518             var index = this.view.findHeaderIndex(hd);
44519             this.view.store.sort(this.view.columns[index].dataIndex);
44520         }
44521     }
44522 });
44523
44524 // Backwards compatibility alias
44525 Ext.ListView.Sorter = Ext.list.Sorter;/**
44526  * @class Ext.TabPanel
44527  * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
44528  * for layout purposes, but also have special support for containing child Components
44529  * (<tt>{@link Ext.Container#items items}</tt>) that are managed using a
44530  * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.</p>
44531  *
44532  * <b>Note:</b> By default, a tab's close tool <i>destroys</i> the child tab Component
44533  * and all its descendants. This makes the child tab Component, and all its descendants <b>unusable</b>. To enable
44534  * re-use of a tab, configure the TabPanel with <b><code>{@link #autoDestroy autoDestroy: false}</code></b>.
44535  *
44536  * <p><b><u>TabPanel header/footer elements</u></b></p>
44537  * <p>TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
44538  * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
44539  * This means that a TabPanel will not display any configured title, and will not display any
44540  * configured header {@link Ext.Panel#tools tools}.</p>
44541  * <p>To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
44542  * <b><tt>{@link Ext.Container#layout layout:'fit'}</tt></b>.</p>
44543  *
44544  * <p><b><u>Tab Events</u></b></p>
44545  * <p>There is no actual tab class &mdash; each tab is simply a {@link Ext.BoxComponent Component}
44546  * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
44547  * can fire additional events that only exist for tabs and are not available from other Components.
44548  * These events are:</p>
44549  * <div><ul class="mdetail-params">
44550  * <li><tt><b>{@link Ext.Panel#activate activate}</b></tt> : Fires when this Component becomes
44551  * the active tab.</li>
44552  * <li><tt><b>{@link Ext.Panel#deactivate deactivate}</b></tt> : Fires when the Component that
44553  * was the active tab becomes deactivated.</li>
44554  * <li><tt><b>{@link Ext.Panel#beforeclose beforeclose}</b></tt> : Fires when the user clicks on the close tool of a closeable tab.
44555  * May be vetoed by returning <code>false</code> from a handler.</li>
44556  * <li><tt><b>{@link Ext.Panel#close close}</b></tt> : Fires a closeable tab has been closed by the user.</li>
44557  * </ul></div>
44558  * <p><b><u>Creating TabPanels from Code</u></b></p>
44559  * <p>TabPanels can be created and rendered completely in code, as in this example:</p>
44560  * <pre><code>
44561 var tabs = new Ext.TabPanel({
44562     renderTo: Ext.getBody(),
44563     activeTab: 0,
44564     items: [{
44565         title: 'Tab 1',
44566         html: 'A simple tab'
44567     },{
44568         title: 'Tab 2',
44569         html: 'Another one'
44570     }]
44571 });
44572 </code></pre>
44573  * <p><b><u>Creating TabPanels from Existing Markup</u></b></p>
44574  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.</p>
44575  * <div><ul class="mdetail-params">
44576  *
44577  * <li>Pre-Structured Markup</li>
44578  * <div class="sub-desc">
44579  * <p>A container div with one or more nested tab divs with class <tt>'x-tab'</tt> can be rendered entirely
44580  * from existing markup (See the {@link #autoTabs} example).</p>
44581  * </div>
44582  *
44583  * <li>Un-Structured Markup</li>
44584  * <div class="sub-desc">
44585  * <p>A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id
44586  * which elements should be the container and the tabs. Using this method tab content can be pulled from different
44587  * elements within the page by id regardless of page structure. For example:</p>
44588  * <pre><code>
44589 var tabs = new Ext.TabPanel({
44590     renderTo: 'my-tabs',
44591     activeTab: 0,
44592     items:[
44593         {contentEl:'tab1', title:'Tab 1'},
44594         {contentEl:'tab2', title:'Tab 2'}
44595     ]
44596 });
44597
44598 // Note that the tabs do not have to be nested within the container (although they can be)
44599 &lt;div id="my-tabs">&lt;/div>
44600 &lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
44601 &lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
44602 </code></pre>
44603  * Note that the tab divs in this example contain the class <tt>'x-hide-display'</tt> so that they can be rendered
44604  * deferred without displaying outside the tabs. You could alternately set <tt>{@link #deferredRender} = false </tt>
44605  * to render all content tabs on page load.
44606  * </div>
44607  *
44608  * </ul></div>
44609  *
44610  * @extends Ext.Panel
44611  * @constructor
44612  * @param {Object} config The configuration options
44613  * @xtype tabpanel
44614  */
44615 Ext.TabPanel = Ext.extend(Ext.Panel,  {
44616     /**
44617      * @cfg {Boolean} layoutOnTabChange
44618      * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
44619      * See {@link Ext.layout.CardLayout}.<code>{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}</code>.
44620      */
44621     /**
44622      * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
44623      * class name applied to the tab strip item representing the child Component, allowing special
44624      * styling to be applied.
44625      */
44626     /**
44627      * @cfg {Boolean} deferredRender
44628      * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.Container#items items}</tt>
44629      * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
44630      * <tt>{@link Ext.Container#items items}</tt> as soon as the {@link Ext.layout.CardLayout layout}
44631      * is rendered. If there is a significant amount of content or a lot of heavy controls being
44632      * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
44633      * improve performance.</p>
44634      * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
44635      * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
44636      * configuration value.</p>
44637      * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
44638      * within an unactivated tab will not be available. For example, this means that if the TabPanel
44639      * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
44640      * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
44641      * available to either {@link Ext.form.BasicForm#getValues getValues} or
44642      * {@link Ext.form.BasicForm#setValues setValues}.</p>
44643      */
44644     deferredRender : true,
44645     /**
44646      * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
44647      */
44648     tabWidth : 120,
44649     /**
44650      * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
44651      */
44652     minTabWidth : 30,
44653     /**
44654      * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
44655      * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
44656      * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
44657      */
44658     resizeTabs : false,
44659     /**
44660      * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
44661      * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
44662      */
44663     enableTabScroll : false,
44664     /**
44665      * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
44666      * (defaults to <tt>100</tt>, or if <tt>{@link #resizeTabs} = true</tt>, the calculated tab width).  Only
44667      * applies when <tt>{@link #enableTabScroll} = true</tt>.
44668      */
44669     scrollIncrement : 0,
44670     /**
44671      * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
44672      * continuously pressed (defaults to <tt>400</tt>).
44673      */
44674     scrollRepeatInterval : 400,
44675     /**
44676      * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
44677      * to <tt>.35</tt>). Only applies when <tt>{@link #animScroll} = true</tt>.
44678      */
44679     scrollDuration : 0.35,
44680     /**
44681      * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
44682      * to <tt>true</tt>).  Only applies when <tt>{@link #enableTabScroll} = true</tt>.
44683      */
44684     animScroll : true,
44685     /**
44686      * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <tt>'top'</tt>).
44687      * The only other supported value is <tt>'bottom'</tt>.  <b>Note</b>: tab scrolling is only supported for
44688      * <tt>tabPosition: 'top'</tt>.
44689      */
44690     tabPosition : 'top',
44691     /**
44692      * @cfg {String} baseCls The base CSS class applied to the panel (defaults to <tt>'x-tab-panel'</tt>).
44693      */
44694     baseCls : 'x-tab-panel',
44695     /**
44696      * @cfg {Boolean} autoTabs
44697      * <p><tt>true</tt> to query the DOM for any divs with a class of 'x-tab' to be automatically converted
44698      * to tabs and added to this panel (defaults to <tt>false</tt>).  Note that the query will be executed within
44699      * the scope of the container element only (so that multiple tab panels from markup can be supported via this
44700      * method).</p>
44701      * <p>This method is only possible when the markup is structured correctly as a container with nested divs
44702      * containing the class <tt>'x-tab'</tt>. To create TabPanels without these limitations, or to pull tab content
44703      * from other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
44704      * <p>There are a couple of things to note when using this method:<ul>
44705      * <li>When using the <tt>autoTabs</tt> config (as opposed to passing individual tab configs in the TabPanel's
44706      * {@link #items} collection), you must use <tt>{@link #applyTo}</tt> to correctly use the specified <tt>id</tt>
44707      * as the tab container. The <tt>autoTabs</tt> method <em>replaces</em> existing content with the TabPanel
44708      * components.</li>
44709      * <li>Make sure that you set <tt>{@link #deferredRender}: false</tt> so that the content elements for each
44710      * tab will be rendered into the TabPanel immediately upon page load, otherwise they will not be transformed
44711      * until each tab is activated and will be visible outside the TabPanel.</li>
44712      * </ul>Example usage:</p>
44713      * <pre><code>
44714 var tabs = new Ext.TabPanel({
44715     applyTo: 'my-tabs',
44716     activeTab: 0,
44717     deferredRender: false,
44718     autoTabs: true
44719 });
44720
44721 // This markup will be converted to a TabPanel from the code above
44722 &lt;div id="my-tabs">
44723     &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
44724     &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
44725 &lt;/div>
44726 </code></pre>
44727      */
44728     autoTabs : false,
44729     /**
44730      * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
44731      * <tt>{@link #autoTabs} = true</tt> (defaults to <tt>'div.x-tab'</tt>).  This can be any valid selector
44732      * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
44733      * tab panel only (so that multiple tab panels from markup can be supported on a page).
44734      */
44735     autoTabSelector : 'div.x-tab',
44736     /**
44737      * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
44738      * activated on render (defaults to undefined).
44739      */
44740     activeTab : undefined,
44741     /**
44742      * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
44743      * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
44744      * with either <tt>{@link #resizeTabs}</tt> or scrolling tabs. (defaults to <tt>2</tt>)
44745      */
44746     tabMargin : 2,
44747     /**
44748      * @cfg {Boolean} plain </tt>true</tt> to render the tab strip without a background container image
44749      * (defaults to <tt>false</tt>).
44750      */
44751     plain : false,
44752     /**
44753      * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
44754      * scrolling (defaults to <tt>20</tt>).
44755      */
44756     wheelIncrement : 20,
44757
44758     /*
44759      * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
44760      * It does not generally need to be changed, but can be if external code also uses an id scheme that can
44761      * potentially clash with this one.
44762      */
44763     idDelimiter : '__',
44764
44765     // private
44766     itemCls : 'x-tab-item',
44767
44768     // private config overrides
44769     elements : 'body',
44770     headerAsText : false,
44771     frame : false,
44772     hideBorders :true,
44773
44774     // private
44775     initComponent : function(){
44776         this.frame = false;
44777         Ext.TabPanel.superclass.initComponent.call(this);
44778         this.addEvents(
44779             /**
44780              * @event beforetabchange
44781              * Fires before the active tab changes. Handlers can <tt>return false</tt> to cancel the tab change.
44782              * @param {TabPanel} this
44783              * @param {Panel} newTab The tab being activated
44784              * @param {Panel} currentTab The current active tab
44785              */
44786             'beforetabchange',
44787             /**
44788              * @event tabchange
44789              * Fires after the active tab has changed.
44790              * @param {TabPanel} this
44791              * @param {Panel} tab The new active tab
44792              */
44793             'tabchange',
44794             /**
44795              * @event contextmenu
44796              * Relays the contextmenu event from a tab selector element in the tab strip.
44797              * @param {TabPanel} this
44798              * @param {Panel} tab The target tab
44799              * @param {EventObject} e
44800              */
44801             'contextmenu'
44802         );
44803         /**
44804          * @cfg {Object} layoutConfig
44805          * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
44806          * <code>layoutConfig</code> may be used to configure this layout manager.
44807          * <code>{@link #deferredRender}</code> and <code>{@link #layoutOnTabChange}</code>
44808          * configured on the TabPanel will be applied as configs to the layout manager.
44809          */
44810         this.setLayout(new Ext.layout.CardLayout(Ext.apply({
44811             layoutOnCardChange: this.layoutOnTabChange,
44812             deferredRender: this.deferredRender
44813         }, this.layoutConfig)));
44814
44815         if(this.tabPosition == 'top'){
44816             this.elements += ',header';
44817             this.stripTarget = 'header';
44818         }else {
44819             this.elements += ',footer';
44820             this.stripTarget = 'footer';
44821         }
44822         if(!this.stack){
44823             this.stack = Ext.TabPanel.AccessStack();
44824         }
44825         this.initItems();
44826     },
44827
44828     // private
44829     onRender : function(ct, position){
44830         Ext.TabPanel.superclass.onRender.call(this, ct, position);
44831
44832         if(this.plain){
44833             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
44834             this[pos].addClass('x-tab-panel-'+pos+'-plain');
44835         }
44836
44837         var st = this[this.stripTarget];
44838
44839         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
44840             tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
44841
44842         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
44843         st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
44844         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
44845
44846         // create an empty span with class x-tab-strip-text to force the height of the header element when there's no tabs.
44847         this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge', cn: [{tag: 'span', cls: 'x-tab-strip-text', cn: '&#160;'}]});
44848         this.strip.createChild({cls:'x-clear'});
44849
44850         this.body.addClass('x-tab-panel-body-'+this.tabPosition);
44851
44852         /**
44853          * @cfg {Template/XTemplate} itemTpl <p>(Optional) A {@link Ext.Template Template} or
44854          * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
44855          * <tt>{@link #getTemplateArgs}</tt> to produce a clickable selector element in the tab strip.</p>
44856          * <p>The main element created should be a <tt>&lt;li></tt> element. In order for a click event on
44857          * a selector element to be connected to its item, it must take its <i>id</i> from the TabPanel's
44858          * native <tt>{@link #getTemplateArgs}</tt>.</p>
44859          * <p>The child element which contains the title text must be marked by the CSS class
44860          * <tt>x-tab-strip-inner</tt>.</p>
44861          * <p>To enable closability, the created element should contain an element marked by the CSS class
44862          * <tt>x-tab-strip-close</tt>.</p>
44863          * <p>If a custom <tt>itemTpl</tt> is supplied, it is the developer's responsibility to create CSS
44864          * style rules to create the desired appearance.</p>
44865          * Below is an example of how to create customized tab selector items:<pre><code>
44866 new Ext.TabPanel({
44867     renderTo: document.body,
44868     minTabWidth: 115,
44869     tabWidth: 135,
44870     enableTabScroll: true,
44871     width: 600,
44872     height: 250,
44873     defaults: {autoScroll:true},
44874     itemTpl: new Ext.XTemplate(
44875     '&lt;li class="{cls}" id="{id}" style="overflow:hidden">',
44876          '&lt;tpl if="closable">',
44877             '&lt;a class="x-tab-strip-close">&lt;/a>',
44878          '&lt;/tpl>',
44879          '&lt;a class="x-tab-right" href="#" style="padding-left:6px">',
44880             '&lt;em class="x-tab-left">',
44881                 '&lt;span class="x-tab-strip-inner">',
44882                     '&lt;img src="{src}" style="float:left;margin:3px 3px 0 0">',
44883                     '&lt;span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}&lt;/span>',
44884                 '&lt;/span>',
44885             '&lt;/em>',
44886         '&lt;/a>',
44887     '&lt;/li>'
44888     ),
44889     getTemplateArgs: function(item) {
44890 //      Call the native method to collect the base data. Like the ID!
44891         var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
44892
44893 //      Add stuff used in our template
44894         return Ext.apply(result, {
44895             closable: item.closable,
44896             src: item.iconSrc,
44897             extra: item.extraText || ''
44898         });
44899     },
44900     items: [{
44901         title: 'New Tab 1',
44902         iconSrc: '../shared/icons/fam/grid.png',
44903         html: 'Tab Body 1',
44904         closable: true
44905     }, {
44906         title: 'New Tab 2',
44907         iconSrc: '../shared/icons/fam/grid.png',
44908         html: 'Tab Body 2',
44909         extraText: 'Extra stuff in the tab button'
44910     }]
44911 });
44912 </code></pre>
44913          */
44914         if(!this.itemTpl){
44915             var tt = new Ext.Template(
44916                  '<li class="{cls}" id="{id}"><a class="x-tab-strip-close"></a>',
44917                  '<a class="x-tab-right" href="#"><em class="x-tab-left">',
44918                  '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
44919                  '</em></a></li>'
44920             );
44921             tt.disableFormats = true;
44922             tt.compile();
44923             Ext.TabPanel.prototype.itemTpl = tt;
44924         }
44925
44926         this.items.each(this.initTab, this);
44927     },
44928
44929     // private
44930     afterRender : function(){
44931         Ext.TabPanel.superclass.afterRender.call(this);
44932         if(this.autoTabs){
44933             this.readTabs(false);
44934         }
44935         if(this.activeTab !== undefined){
44936             var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
44937             delete this.activeTab;
44938             this.setActiveTab(item);
44939         }
44940     },
44941
44942     // private
44943     initEvents : function(){
44944         Ext.TabPanel.superclass.initEvents.call(this);
44945         this.mon(this.strip, {
44946             scope: this,
44947             mousedown: this.onStripMouseDown,
44948             contextmenu: this.onStripContextMenu
44949         });
44950         if(this.enableTabScroll){
44951             this.mon(this.strip, 'mousewheel', this.onWheel, this);
44952         }
44953     },
44954
44955     // private
44956     findTargets : function(e){
44957         var item = null,
44958             itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip);
44959
44960         if(itemEl){
44961             item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
44962             if(item.disabled){
44963                 return {
44964                     close : null,
44965                     item : null,
44966                     el : null
44967                 };
44968             }
44969         }
44970         return {
44971             close : e.getTarget('.x-tab-strip-close', this.strip),
44972             item : item,
44973             el : itemEl
44974         };
44975     },
44976
44977     // private
44978     onStripMouseDown : function(e){
44979         if(e.button !== 0){
44980             return;
44981         }
44982         e.preventDefault();
44983         var t = this.findTargets(e);
44984         if(t.close){
44985             if (t.item.fireEvent('beforeclose', t.item) !== false) {
44986                 t.item.fireEvent('close', t.item);
44987                 this.remove(t.item);
44988             }
44989             return;
44990         }
44991         if(t.item && t.item != this.activeTab){
44992             this.setActiveTab(t.item);
44993         }
44994     },
44995
44996     // private
44997     onStripContextMenu : function(e){
44998         e.preventDefault();
44999         var t = this.findTargets(e);
45000         if(t.item){
45001             this.fireEvent('contextmenu', this, t.item, e);
45002         }
45003     },
45004
45005     /**
45006      * True to scan the markup in this tab panel for <tt>{@link #autoTabs}</tt> using the
45007      * <tt>{@link #autoTabSelector}</tt>
45008      * @param {Boolean} removeExisting True to remove existing tabs
45009      */
45010     readTabs : function(removeExisting){
45011         if(removeExisting === true){
45012             this.items.each(function(item){
45013                 this.remove(item);
45014             }, this);
45015         }
45016         var tabs = this.el.query(this.autoTabSelector);
45017         for(var i = 0, len = tabs.length; i < len; i++){
45018             var tab = tabs[i],
45019                 title = tab.getAttribute('title');
45020             tab.removeAttribute('title');
45021             this.add({
45022                 title: title,
45023                 contentEl: tab
45024             });
45025         }
45026     },
45027
45028     // private
45029     initTab : function(item, index){
45030         var before = this.strip.dom.childNodes[index],
45031             p = this.getTemplateArgs(item),
45032             el = before ?
45033                  this.itemTpl.insertBefore(before, p) :
45034                  this.itemTpl.append(this.strip, p),
45035             cls = 'x-tab-strip-over',
45036             tabEl = Ext.get(el);
45037
45038         tabEl.hover(function(){
45039             if(!item.disabled){
45040                 tabEl.addClass(cls);
45041             }
45042         }, function(){
45043             tabEl.removeClass(cls);
45044         });
45045
45046         if(item.tabTip){
45047             tabEl.child('span.x-tab-strip-text', true).qtip = item.tabTip;
45048         }
45049         item.tabEl = el;
45050
45051         // Route *keyboard triggered* click events to the tab strip mouse handler.
45052         tabEl.select('a').on('click', function(e){
45053             if(!e.getPageX()){
45054                 this.onStripMouseDown(e);
45055             }
45056         }, this, {preventDefault: true});
45057
45058         item.on({
45059             scope: this,
45060             disable: this.onItemDisabled,
45061             enable: this.onItemEnabled,
45062             titlechange: this.onItemTitleChanged,
45063             iconchange: this.onItemIconChanged,
45064             beforeshow: this.onBeforeShowItem
45065         });
45066     },
45067
45068
45069
45070     /**
45071      * <p>Provides template arguments for rendering a tab selector item in the tab strip.</p>
45072      * <p>This method returns an object hash containing properties used by the TabPanel's <tt>{@link #itemTpl}</tt>
45073      * to create a formatted, clickable tab selector element. The properties which must be returned
45074      * are:</p><div class="mdetail-params"><ul>
45075      * <li><b>id</b> : String<div class="sub-desc">A unique identifier which links to the item</div></li>
45076      * <li><b>text</b> : String<div class="sub-desc">The text to display</div></li>
45077      * <li><b>cls</b> : String<div class="sub-desc">The CSS class name</div></li>
45078      * <li><b>iconCls</b> : String<div class="sub-desc">A CSS class to provide appearance for an icon.</div></li>
45079      * </ul></div>
45080      * @param {Ext.BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
45081      * @return {Object} An object hash containing the properties required to render the selector element.
45082      */
45083     getTemplateArgs : function(item) {
45084         var cls = item.closable ? 'x-tab-strip-closable' : '';
45085         if(item.disabled){
45086             cls += ' x-item-disabled';
45087         }
45088         if(item.iconCls){
45089             cls += ' x-tab-with-icon';
45090         }
45091         if(item.tabCls){
45092             cls += ' ' + item.tabCls;
45093         }
45094
45095         return {
45096             id: this.id + this.idDelimiter + item.getItemId(),
45097             text: item.title,
45098             cls: cls,
45099             iconCls: item.iconCls || ''
45100         };
45101     },
45102
45103     // private
45104     onAdd : function(c){
45105         Ext.TabPanel.superclass.onAdd.call(this, c);
45106         if(this.rendered){
45107             var items = this.items;
45108             this.initTab(c, items.indexOf(c));
45109             if(items.getCount() == 1 && !this.collapsed){
45110                 this.syncSize();
45111             }
45112             this.delegateUpdates();
45113         }
45114     },
45115
45116     // private
45117     onBeforeAdd : function(item){
45118         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
45119         if(existing){
45120             this.setActiveTab(item);
45121             return false;
45122         }
45123         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
45124         var es = item.elements;
45125         item.elements = es ? es.replace(',header', '') : es;
45126         item.border = (item.border === true);
45127     },
45128
45129     // private
45130     onRemove : function(c){
45131         var te = Ext.get(c.tabEl);
45132         // check if the tabEl exists, it won't if the tab isn't rendered
45133         if(te){
45134             te.select('a').removeAllListeners();
45135             Ext.destroy(te);
45136         }
45137         Ext.TabPanel.superclass.onRemove.call(this, c);
45138         this.stack.remove(c);
45139         delete c.tabEl;
45140         c.un('disable', this.onItemDisabled, this);
45141         c.un('enable', this.onItemEnabled, this);
45142         c.un('titlechange', this.onItemTitleChanged, this);
45143         c.un('iconchange', this.onItemIconChanged, this);
45144         c.un('beforeshow', this.onBeforeShowItem, this);
45145         if(c == this.activeTab){
45146             var next = this.stack.next();
45147             if(next){
45148                 this.setActiveTab(next);
45149             }else if(this.items.getCount() > 0){
45150                 this.setActiveTab(0);
45151             }else{
45152                 this.setActiveTab(null);
45153             }
45154         }
45155         if(!this.destroying){
45156             this.delegateUpdates();
45157         }
45158     },
45159
45160     // private
45161     onBeforeShowItem : function(item){
45162         if(item != this.activeTab){
45163             this.setActiveTab(item);
45164             return false;
45165         }
45166     },
45167
45168     // private
45169     onItemDisabled : function(item){
45170         var el = this.getTabEl(item);
45171         if(el){
45172             Ext.fly(el).addClass('x-item-disabled');
45173         }
45174         this.stack.remove(item);
45175     },
45176
45177     // private
45178     onItemEnabled : function(item){
45179         var el = this.getTabEl(item);
45180         if(el){
45181             Ext.fly(el).removeClass('x-item-disabled');
45182         }
45183     },
45184
45185     // private
45186     onItemTitleChanged : function(item){
45187         var el = this.getTabEl(item);
45188         if(el){
45189             Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
45190         }
45191     },
45192
45193     //private
45194     onItemIconChanged : function(item, iconCls, oldCls){
45195         var el = this.getTabEl(item);
45196         if(el){
45197             el = Ext.get(el);
45198             el.child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
45199             el[Ext.isEmpty(iconCls) ? 'removeClass' : 'addClass']('x-tab-with-icon');
45200         }
45201     },
45202
45203     /**
45204      * Gets the DOM element for the tab strip item which activates the child panel with the specified
45205      * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
45206      * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
45207      * @return {HTMLElement} The DOM node
45208      */
45209     getTabEl : function(item){
45210         var c = this.getComponent(item);
45211         return c ? c.tabEl : null;
45212     },
45213
45214     // private
45215     onResize : function(){
45216         Ext.TabPanel.superclass.onResize.apply(this, arguments);
45217         this.delegateUpdates();
45218     },
45219
45220     /**
45221      * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
45222      */
45223     beginUpdate : function(){
45224         this.suspendUpdates = true;
45225     },
45226
45227     /**
45228      * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
45229      */
45230     endUpdate : function(){
45231         this.suspendUpdates = false;
45232         this.delegateUpdates();
45233     },
45234
45235     /**
45236      * Hides the tab strip item for the passed tab
45237      * @param {Number/String/Panel} item The tab index, id or item
45238      */
45239     hideTabStripItem : function(item){
45240         item = this.getComponent(item);
45241         var el = this.getTabEl(item);
45242         if(el){
45243             el.style.display = 'none';
45244             this.delegateUpdates();
45245         }
45246         this.stack.remove(item);
45247     },
45248
45249     /**
45250      * Unhides the tab strip item for the passed tab
45251      * @param {Number/String/Panel} item The tab index, id or item
45252      */
45253     unhideTabStripItem : function(item){
45254         item = this.getComponent(item);
45255         var el = this.getTabEl(item);
45256         if(el){
45257             el.style.display = '';
45258             this.delegateUpdates();
45259         }
45260     },
45261
45262     // private
45263     delegateUpdates : function(){
45264         if(this.suspendUpdates){
45265             return;
45266         }
45267         if(this.resizeTabs && this.rendered){
45268             this.autoSizeTabs();
45269         }
45270         if(this.enableTabScroll && this.rendered){
45271             this.autoScrollTabs();
45272         }
45273     },
45274
45275     // private
45276     autoSizeTabs : function(){
45277         var count = this.items.length,
45278             ce = this.tabPosition != 'bottom' ? 'header' : 'footer',
45279             ow = this[ce].dom.offsetWidth,
45280             aw = this[ce].dom.clientWidth;
45281
45282         if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
45283             return;
45284         }
45285
45286         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
45287         this.lastTabWidth = each;
45288         var lis = this.strip.query('li:not(.x-tab-edge)');
45289         for(var i = 0, len = lis.length; i < len; i++) {
45290             var li = lis[i],
45291                 inner = Ext.fly(li).child('.x-tab-strip-inner', true),
45292                 tw = li.offsetWidth,
45293                 iw = inner.offsetWidth;
45294             inner.style.width = (each - (tw-iw)) + 'px';
45295         }
45296     },
45297
45298     // private
45299     adjustBodyWidth : function(w){
45300         if(this.header){
45301             this.header.setWidth(w);
45302         }
45303         if(this.footer){
45304             this.footer.setWidth(w);
45305         }
45306         return w;
45307     },
45308
45309     /**
45310      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
45311      * can <tt>return false</tt> to cancel the tab change.
45312      * @param {String/Number} item
45313      * The id or tab Panel to activate. This parameter may be any of the following:
45314      * <div><ul class="mdetail-params">
45315      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
45316      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
45317      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
45318      * within the <code>{@link Ext.Container#items items}</code> <b>property</b></li>
45319      * </ul></div>
45320      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
45321      */
45322     setActiveTab : function(item){
45323         item = this.getComponent(item);
45324         if(this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
45325             return;
45326         }
45327         if(!this.rendered){
45328             this.activeTab = item;
45329             return;
45330         }
45331         if(this.activeTab != item){
45332             if(this.activeTab){
45333                 var oldEl = this.getTabEl(this.activeTab);
45334                 if(oldEl){
45335                     Ext.fly(oldEl).removeClass('x-tab-strip-active');
45336                 }
45337             }
45338             if(item){
45339                 var el = this.getTabEl(item);
45340                 Ext.fly(el).addClass('x-tab-strip-active');
45341                 this.activeTab = item;
45342                 this.stack.add(item);
45343
45344                 this.layout.setActiveItem(item);
45345                 if(this.scrolling){
45346                     this.scrollToTab(item, this.animScroll);
45347                 }
45348             }
45349             this.fireEvent('tabchange', this, item);
45350         }
45351     },
45352
45353     /**
45354      * Returns the Component which is the currently active tab. <b>Note that before the TabPanel
45355      * first activates a child Component, this method will return whatever was configured in the
45356      * {@link #activeTab} config option.</b>
45357      * @return {BoxComponent} The currently active child Component if one <i>is</i> active, or the {@link #activeTab} config value.
45358      */
45359     getActiveTab : function(){
45360         return this.activeTab || null;
45361     },
45362
45363     /**
45364      * Gets the specified tab by id.
45365      * @param {String} id The tab id
45366      * @return {Panel} The tab
45367      */
45368     getItem : function(item){
45369         return this.getComponent(item);
45370     },
45371
45372     // private
45373     autoScrollTabs : function(){
45374         this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
45375         var count = this.items.length,
45376             ow = this.pos.dom.offsetWidth,
45377             tw = this.pos.dom.clientWidth,
45378             wrap = this.stripWrap,
45379             wd = wrap.dom,
45380             cw = wd.offsetWidth,
45381             pos = this.getScrollPos(),
45382             l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
45383
45384         if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
45385             return;
45386         }
45387         if(l <= tw){
45388             wd.scrollLeft = 0;
45389             wrap.setWidth(tw);
45390             if(this.scrolling){
45391                 this.scrolling = false;
45392                 this.pos.removeClass('x-tab-scrolling');
45393                 this.scrollLeft.hide();
45394                 this.scrollRight.hide();
45395                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
45396                 if(Ext.isAir || Ext.isWebKit){
45397                     wd.style.marginLeft = '';
45398                     wd.style.marginRight = '';
45399                 }
45400             }
45401         }else{
45402             if(!this.scrolling){
45403                 this.pos.addClass('x-tab-scrolling');
45404                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
45405                 if(Ext.isAir || Ext.isWebKit){
45406                     wd.style.marginLeft = '18px';
45407                     wd.style.marginRight = '18px';
45408                 }
45409             }
45410             tw -= wrap.getMargins('lr');
45411             wrap.setWidth(tw > 20 ? tw : 20);
45412             if(!this.scrolling){
45413                 if(!this.scrollLeft){
45414                     this.createScrollers();
45415                 }else{
45416                     this.scrollLeft.show();
45417                     this.scrollRight.show();
45418                 }
45419             }
45420             this.scrolling = true;
45421             if(pos > (l-tw)){ // ensure it stays within bounds
45422                 wd.scrollLeft = l-tw;
45423             }else{ // otherwise, make sure the active tab is still visible
45424                 this.scrollToTab(this.activeTab, false);
45425             }
45426             this.updateScrollButtons();
45427         }
45428     },
45429
45430     // private
45431     createScrollers : function(){
45432         this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
45433         var h = this.stripWrap.dom.offsetHeight;
45434
45435         // left
45436         var sl = this.pos.insertFirst({
45437             cls:'x-tab-scroller-left'
45438         });
45439         sl.setHeight(h);
45440         sl.addClassOnOver('x-tab-scroller-left-over');
45441         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
45442             interval : this.scrollRepeatInterval,
45443             handler: this.onScrollLeft,
45444             scope: this
45445         });
45446         this.scrollLeft = sl;
45447
45448         // right
45449         var sr = this.pos.insertFirst({
45450             cls:'x-tab-scroller-right'
45451         });
45452         sr.setHeight(h);
45453         sr.addClassOnOver('x-tab-scroller-right-over');
45454         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
45455             interval : this.scrollRepeatInterval,
45456             handler: this.onScrollRight,
45457             scope: this
45458         });
45459         this.scrollRight = sr;
45460     },
45461
45462     // private
45463     getScrollWidth : function(){
45464         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
45465     },
45466
45467     // private
45468     getScrollPos : function(){
45469         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
45470     },
45471
45472     // private
45473     getScrollArea : function(){
45474         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
45475     },
45476
45477     // private
45478     getScrollAnim : function(){
45479         return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
45480     },
45481
45482     // private
45483     getScrollIncrement : function(){
45484         return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
45485     },
45486
45487     /**
45488      * Scrolls to a particular tab if tab scrolling is enabled
45489      * @param {Panel} item The item to scroll to
45490      * @param {Boolean} animate True to enable animations
45491      */
45492
45493     scrollToTab : function(item, animate){
45494         if(!item){
45495             return;
45496         }
45497         var el = this.getTabEl(item),
45498             pos = this.getScrollPos(),
45499             area = this.getScrollArea(),
45500             left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos,
45501             right = left + el.offsetWidth;
45502         if(left < pos){
45503             this.scrollTo(left, animate);
45504         }else if(right > (pos + area)){
45505             this.scrollTo(right - area, animate);
45506         }
45507     },
45508
45509     // private
45510     scrollTo : function(pos, animate){
45511         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
45512         if(!animate){
45513             this.updateScrollButtons();
45514         }
45515     },
45516
45517     onWheel : function(e){
45518         var d = e.getWheelDelta()*this.wheelIncrement*-1;
45519         e.stopEvent();
45520
45521         var pos = this.getScrollPos(),
45522             newpos = pos + d,
45523             sw = this.getScrollWidth()-this.getScrollArea();
45524
45525         var s = Math.max(0, Math.min(sw, newpos));
45526         if(s != pos){
45527             this.scrollTo(s, false);
45528         }
45529     },
45530
45531     // private
45532     onScrollRight : function(){
45533         var sw = this.getScrollWidth()-this.getScrollArea(),
45534             pos = this.getScrollPos(),
45535             s = Math.min(sw, pos + this.getScrollIncrement());
45536         if(s != pos){
45537             this.scrollTo(s, this.animScroll);
45538         }
45539     },
45540
45541     // private
45542     onScrollLeft : function(){
45543         var pos = this.getScrollPos(),
45544             s = Math.max(0, pos - this.getScrollIncrement());
45545         if(s != pos){
45546             this.scrollTo(s, this.animScroll);
45547         }
45548     },
45549
45550     // private
45551     updateScrollButtons : function(){
45552         var pos = this.getScrollPos();
45553         this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
45554         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
45555     },
45556
45557     // private
45558     beforeDestroy : function() {
45559         Ext.destroy(this.leftRepeater, this.rightRepeater);
45560         this.deleteMembers('strip', 'edge', 'scrollLeft', 'scrollRight', 'stripWrap');
45561         this.activeTab = null;
45562         Ext.TabPanel.superclass.beforeDestroy.apply(this);
45563     }
45564
45565     /**
45566      * @cfg {Boolean} collapsible
45567      * @hide
45568      */
45569     /**
45570      * @cfg {String} header
45571      * @hide
45572      */
45573     /**
45574      * @cfg {Boolean} headerAsText
45575      * @hide
45576      */
45577     /**
45578      * @property header
45579      * @hide
45580      */
45581     /**
45582      * @cfg title
45583      * @hide
45584      */
45585     /**
45586      * @cfg {Array} tools
45587      * @hide
45588      */
45589     /**
45590      * @cfg {Array} toolTemplate
45591      * @hide
45592      */
45593     /**
45594      * @cfg {Boolean} hideCollapseTool
45595      * @hide
45596      */
45597     /**
45598      * @cfg {Boolean} titleCollapse
45599      * @hide
45600      */
45601     /**
45602      * @cfg {Boolean} collapsed
45603      * @hide
45604      */
45605     /**
45606      * @cfg {String} layout
45607      * @hide
45608      */
45609     /**
45610      * @cfg {Boolean} preventBodyReset
45611      * @hide
45612      */
45613 });
45614 Ext.reg('tabpanel', Ext.TabPanel);
45615
45616 /**
45617  * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
45618  * the {@link #beforetabchange} event which can <tt>return false</tt> to cancel the tab change.
45619  * @param {String/Panel} tab The id or tab Panel to activate
45620  * @method activate
45621  */
45622 Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
45623
45624 // private utility class used by TabPanel
45625 Ext.TabPanel.AccessStack = function(){
45626     var items = [];
45627     return {
45628         add : function(item){
45629             items.push(item);
45630             if(items.length > 10){
45631                 items.shift();
45632             }
45633         },
45634
45635         remove : function(item){
45636             var s = [];
45637             for(var i = 0, len = items.length; i < len; i++) {
45638                 if(items[i] != item){
45639                     s.push(items[i]);
45640                 }
45641             }
45642             items = s;
45643         },
45644
45645         next : function(){
45646             return items.pop();
45647         }
45648     };
45649 };
45650 /**
45651  * @class Ext.Button
45652  * @extends Ext.BoxComponent
45653  * Simple Button class
45654  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
45655  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
45656  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
45657  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
45658  * The handler is passed the following parameters:<div class="mdetail-params"><ul>
45659  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
45660  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
45661  * </ul></div>
45662  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
45663  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
45664  * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object
45665  * @cfg {Boolean} hidden True to start hidden (defaults to false)
45666  * @cfg {Boolean} disabled True to start disabled (defaults to false)
45667  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
45668  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
45669  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
45670  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
45671  * @constructor
45672  * Create a new button
45673  * @param {Object} config The config object
45674  * @xtype button
45675  */
45676 Ext.Button = Ext.extend(Ext.BoxComponent, {
45677     /**
45678      * Read-only. True if this button is hidden
45679      * @type Boolean
45680      */
45681     hidden : false,
45682     /**
45683      * Read-only. True if this button is disabled
45684      * @type Boolean
45685      */
45686     disabled : false,
45687     /**
45688      * Read-only. True if this button is pressed (only if enableToggle = true)
45689      * @type Boolean
45690      */
45691     pressed : false,
45692
45693     /**
45694      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
45695      */
45696
45697     /**
45698      * @cfg {Boolean} allowDepress
45699      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
45700      */
45701
45702     /**
45703      * @cfg {Boolean} enableToggle
45704      * True to enable pressed/not pressed toggling (defaults to false)
45705      */
45706     enableToggle : false,
45707     /**
45708      * @cfg {Function} toggleHandler
45709      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
45710      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
45711      * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>
45712      * </ul>
45713      */
45714     /**
45715      * @cfg {Mixed} menu
45716      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
45717      */
45718     /**
45719      * @cfg {String} menuAlign
45720      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
45721      */
45722     menuAlign : 'tl-bl?',
45723
45724     /**
45725      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
45726      * text to be used if this item is shown in the overflow menu. See also
45727      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
45728      */
45729     /**
45730      * @cfg {String} iconCls
45731      * A css class which sets a background image to be used as the icon for this button
45732      */
45733     /**
45734      * @cfg {String} type
45735      * submit, reset or button - defaults to 'button'
45736      */
45737     type : 'button',
45738
45739     // private
45740     menuClassTarget : 'tr:nth(2)',
45741
45742     /**
45743      * @cfg {String} clickEvent
45744      * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
45745      * Defaults to <tt>'click'</tt>.
45746      */
45747     clickEvent : 'click',
45748
45749     /**
45750      * @cfg {Boolean} handleMouseEvents
45751      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
45752      */
45753     handleMouseEvents : true,
45754
45755     /**
45756      * @cfg {String} tooltipType
45757      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
45758      */
45759     tooltipType : 'qtip',
45760
45761     /**
45762      * @cfg {String} buttonSelector
45763      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
45764      * DOM structure created.</p>
45765      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
45766      * a focussable element.</p>
45767      * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
45768      */
45769     buttonSelector : 'button:first-child',
45770
45771     /**
45772      * @cfg {String} scale
45773      * <p>(Optional) The size of the Button. Three values are allowed:</p>
45774      * <ul class="mdetail-params">
45775      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
45776      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
45777      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
45778      * </ul>
45779      * <p>Defaults to <b><tt>'small'</tt></b>.</p>
45780      */
45781     scale : 'small',
45782
45783     /**
45784      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
45785      * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
45786      * executed. Defaults to this Button.
45787      */
45788
45789     /**
45790      * @cfg {String} iconAlign
45791      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
45792      * <ul class="mdetail-params">
45793      * <li>'top'<div class="sub-desc"></div></li>
45794      * <li>'right'<div class="sub-desc"></div></li>
45795      * <li>'bottom'<div class="sub-desc"></div></li>
45796      * <li>'left'<div class="sub-desc"></div></li>
45797      * </ul>
45798      * <p>Defaults to <b><tt>'left'</tt></b>.</p>
45799      */
45800     iconAlign : 'left',
45801
45802     /**
45803      * @cfg {String} arrowAlign
45804      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
45805      * Two values are allowed:</p>
45806      * <ul class="mdetail-params">
45807      * <li>'right'<div class="sub-desc"></div></li>
45808      * <li>'bottom'<div class="sub-desc"></div></li>
45809      * </ul>
45810      * <p>Defaults to <b><tt>'right'</tt></b>.</p>
45811      */
45812     arrowAlign : 'right',
45813
45814     /**
45815      * @cfg {Ext.Template} template (Optional)
45816      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
45817      * Instances, or subclasses which need a different DOM structure may provide a different
45818      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
45819      * @type Ext.Template
45820      * @property template
45821      */
45822     /**
45823      * @cfg {String} cls
45824      * A CSS class string to apply to the button's main element.
45825      */
45826     /**
45827      * @property menu
45828      * @type Menu
45829      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
45830      */
45831
45832     initComponent : function(){
45833         Ext.Button.superclass.initComponent.call(this);
45834
45835         this.addEvents(
45836             /**
45837              * @event click
45838              * Fires when this button is clicked
45839              * @param {Button} this
45840              * @param {EventObject} e The click event
45841              */
45842             'click',
45843             /**
45844              * @event toggle
45845              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
45846              * @param {Button} this
45847              * @param {Boolean} pressed
45848              */
45849             'toggle',
45850             /**
45851              * @event mouseover
45852              * Fires when the mouse hovers over the button
45853              * @param {Button} this
45854              * @param {Event} e The event object
45855              */
45856             'mouseover',
45857             /**
45858              * @event mouseout
45859              * Fires when the mouse exits the button
45860              * @param {Button} this
45861              * @param {Event} e The event object
45862              */
45863             'mouseout',
45864             /**
45865              * @event menushow
45866              * If this button has a menu, this event fires when it is shown
45867              * @param {Button} this
45868              * @param {Menu} menu
45869              */
45870             'menushow',
45871             /**
45872              * @event menuhide
45873              * If this button has a menu, this event fires when it is hidden
45874              * @param {Button} this
45875              * @param {Menu} menu
45876              */
45877             'menuhide',
45878             /**
45879              * @event menutriggerover
45880              * If this button has a menu, this event fires when the mouse enters the menu triggering element
45881              * @param {Button} this
45882              * @param {Menu} menu
45883              * @param {EventObject} e
45884              */
45885             'menutriggerover',
45886             /**
45887              * @event menutriggerout
45888              * If this button has a menu, this event fires when the mouse leaves the menu triggering element
45889              * @param {Button} this
45890              * @param {Menu} menu
45891              * @param {EventObject} e
45892              */
45893             'menutriggerout'
45894         );
45895         if(this.menu){
45896             this.menu = Ext.menu.MenuMgr.get(this.menu);
45897         }
45898         if(Ext.isString(this.toggleGroup)){
45899             this.enableToggle = true;
45900         }
45901     },
45902
45903 /**
45904   * <p>This method returns an Array which provides substitution parameters for the {@link #template Template} used
45905   * to create this Button's DOM structure.</p>
45906   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
45907   * own implementation of this method.</p>
45908   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
45909   * following items:</p><div class="mdetail-params"><ul>
45910   * <li>The &lt;button&gt;'s {@link #type}</li>
45911   * <li>A CSS class name applied to the Button's main &lt;tbody&gt; element which determines the button's scale and icon alignment.</li>
45912   * <li>A CSS class to determine the presence and position of an arrow icon. (<code>'x-btn-arrow'</code> or <code>'x-btn-arrow-bottom'</code> or <code>''</code>)</li>
45913   * <li>The {@link #cls} CSS class name applied to the button's wrapping &lt;table&gt; element.</li>
45914   * <li>The Component id which is applied to the button's wrapping &lt;table&gt; element.</li>
45915   * </ul></div>
45916   * @return {Array} Substitution data for a Template.
45917  */
45918     getTemplateArgs : function(){
45919         return [this.type, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass(), this.cls, this.id];
45920     },
45921
45922     // private
45923     setButtonClass : function(){
45924         if(this.useSetClass){
45925             if(!Ext.isEmpty(this.oldCls)){
45926                 this.el.removeClass([this.oldCls, 'x-btn-pressed']);
45927             }
45928             this.oldCls = (this.iconCls || this.icon) ? (this.text ? ' x-btn-text-icon' : ' x-btn-icon') : ' x-btn-noicon';
45929             this.el.addClass([this.oldCls, this.pressed ? 'x-btn-pressed' : null]);
45930         }
45931     },
45932
45933     // protected
45934     getMenuClass : function(){
45935         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
45936     },
45937
45938     // private
45939     onRender : function(ct, position){
45940         if(!this.template){
45941             if(!Ext.Button.buttonTemplate){
45942                 // hideous table template
45943                 Ext.Button.buttonTemplate = new Ext.Template(
45944                     '<table id="{4}" cellspacing="0" class="x-btn {3}"><tbody class="{1}">',
45945                     '<tr><td class="x-btn-tl"><i>&#160;</i></td><td class="x-btn-tc"></td><td class="x-btn-tr"><i>&#160;</i></td></tr>',
45946                     '<tr><td class="x-btn-ml"><i>&#160;</i></td><td class="x-btn-mc"><em class="{2}" unselectable="on"><button type="{0}"></button></em></td><td class="x-btn-mr"><i>&#160;</i></td></tr>',
45947                     '<tr><td class="x-btn-bl"><i>&#160;</i></td><td class="x-btn-bc"></td><td class="x-btn-br"><i>&#160;</i></td></tr>',
45948                     '</tbody></table>');
45949                 Ext.Button.buttonTemplate.compile();
45950             }
45951             this.template = Ext.Button.buttonTemplate;
45952         }
45953
45954         var btn, targs = this.getTemplateArgs();
45955
45956         if(position){
45957             btn = this.template.insertBefore(position, targs, true);
45958         }else{
45959             btn = this.template.append(ct, targs, true);
45960         }
45961         /**
45962          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
45963          * this references a <tt>&lt;button&gt;</tt> element. Read only.
45964          * @type Ext.Element
45965          * @property btnEl
45966          */
45967         this.btnEl = btn.child(this.buttonSelector);
45968         this.mon(this.btnEl, {
45969             scope: this,
45970             focus: this.onFocus,
45971             blur: this.onBlur
45972         });
45973
45974         this.initButtonEl(btn, this.btnEl);
45975
45976         Ext.ButtonToggleMgr.register(this);
45977     },
45978
45979     // private
45980     initButtonEl : function(btn, btnEl){
45981         this.el = btn;
45982         this.setIcon(this.icon);
45983         this.setText(this.text);
45984         this.setIconClass(this.iconCls);
45985         if(Ext.isDefined(this.tabIndex)){
45986             btnEl.dom.tabIndex = this.tabIndex;
45987         }
45988         if(this.tooltip){
45989             this.setTooltip(this.tooltip, true);
45990         }
45991
45992         if(this.handleMouseEvents){
45993             this.mon(btn, {
45994                 scope: this,
45995                 mouseover: this.onMouseOver,
45996                 mousedown: this.onMouseDown
45997             });
45998
45999             // new functionality for monitoring on the document level
46000             //this.mon(btn, 'mouseout', this.onMouseOut, this);
46001         }
46002
46003         if(this.menu){
46004             this.mon(this.menu, {
46005                 scope: this,
46006                 show: this.onMenuShow,
46007                 hide: this.onMenuHide
46008             });
46009         }
46010
46011         if(this.repeat){
46012             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
46013             this.mon(repeater, 'click', this.onClick, this);
46014         }
46015         this.mon(btn, this.clickEvent, this.onClick, this);
46016     },
46017
46018     // private
46019     afterRender : function(){
46020         Ext.Button.superclass.afterRender.call(this);
46021         this.useSetClass = true;
46022         this.setButtonClass();
46023         this.doc = Ext.getDoc();
46024         this.doAutoWidth();
46025     },
46026
46027     /**
46028      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
46029      * the value of the {@link iconCls} config internally.
46030      * @param {String} cls The CSS class providing the icon image
46031      * @return {Ext.Button} this
46032      */
46033     setIconClass : function(cls){
46034         this.iconCls = cls;
46035         if(this.el){
46036             this.btnEl.dom.className = '';
46037             this.btnEl.addClass(['x-btn-text', cls || '']);
46038             this.setButtonClass();
46039         }
46040         return this;
46041     },
46042
46043     /**
46044      * Sets the tooltip for this Button.
46045      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
46046      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
46047      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
46048      * </ul></div>
46049      * @return {Ext.Button} this
46050      */
46051     setTooltip : function(tooltip, /* private */ initial){
46052         if(this.rendered){
46053             if(!initial){
46054                 this.clearTip();
46055             }
46056             if(Ext.isObject(tooltip)){
46057                 Ext.QuickTips.register(Ext.apply({
46058                       target: this.btnEl.id
46059                 }, tooltip));
46060                 this.tooltip = tooltip;
46061             }else{
46062                 this.btnEl.dom[this.tooltipType] = tooltip;
46063             }
46064         }else{
46065             this.tooltip = tooltip;
46066         }
46067         return this;
46068     },
46069
46070     // private
46071     clearTip : function(){
46072         if(Ext.isObject(this.tooltip)){
46073             Ext.QuickTips.unregister(this.btnEl);
46074         }
46075     },
46076
46077     // private
46078     beforeDestroy : function(){
46079         if(this.rendered){
46080             this.clearTip();
46081         }
46082         if(this.menu && this.destroyMenu !== false) {
46083             Ext.destroy(this.menu);
46084         }
46085         Ext.destroy(this.repeater);
46086     },
46087
46088     // private
46089     onDestroy : function(){
46090         if(this.rendered){
46091             this.doc.un('mouseover', this.monitorMouseOver, this);
46092             this.doc.un('mouseup', this.onMouseUp, this);
46093             delete this.doc;
46094             delete this.btnEl;
46095             Ext.ButtonToggleMgr.unregister(this);
46096         }
46097         Ext.Button.superclass.onDestroy.call(this);
46098     },
46099
46100     // private
46101     doAutoWidth : function(){
46102         if(this.el && this.text && this.width === undefined){
46103             this.el.setWidth('auto');
46104             if(Ext.isIE7 && Ext.isStrict){
46105                 var ib = this.btnEl;
46106                 if(ib && ib.getWidth() > 20){
46107                     ib.clip();
46108                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
46109                 }
46110             }
46111             if(this.minWidth){
46112                 if(this.el.getWidth() < this.minWidth){
46113                     this.el.setWidth(this.minWidth);
46114                 }
46115             }
46116         }
46117     },
46118
46119     /**
46120      * Assigns this Button's click handler
46121      * @param {Function} handler The function to call when the button is clicked
46122      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
46123      * Defaults to this Button.
46124      * @return {Ext.Button} this
46125      */
46126     setHandler : function(handler, scope){
46127         this.handler = handler;
46128         this.scope = scope;
46129         return this;
46130     },
46131
46132     /**
46133      * Sets this Button's text
46134      * @param {String} text The button text
46135      * @return {Ext.Button} this
46136      */
46137     setText : function(text){
46138         this.text = text;
46139         if(this.el){
46140             this.btnEl.update(text || '&#160;');
46141             this.setButtonClass();
46142         }
46143         this.doAutoWidth();
46144         return this;
46145     },
46146
46147     /**
46148      * Sets the background image (inline style) of the button.  This method also changes
46149      * the value of the {@link icon} config internally.
46150      * @param {String} icon The path to an image to display in the button
46151      * @return {Ext.Button} this
46152      */
46153     setIcon : function(icon){
46154         this.icon = icon;
46155         if(this.el){
46156             this.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
46157             this.setButtonClass();
46158         }
46159         return this;
46160     },
46161
46162     /**
46163      * Gets the text for this Button
46164      * @return {String} The button text
46165      */
46166     getText : function(){
46167         return this.text;
46168     },
46169
46170     /**
46171      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
46172      * @param {Boolean} state (optional) Force a particular state
46173      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
46174      * @return {Ext.Button} this
46175      */
46176     toggle : function(state, suppressEvent){
46177         state = state === undefined ? !this.pressed : !!state;
46178         if(state != this.pressed){
46179             if(this.rendered){
46180                 this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
46181             }
46182             this.pressed = state;
46183             if(!suppressEvent){
46184                 this.fireEvent('toggle', this, state);
46185                 if(this.toggleHandler){
46186                     this.toggleHandler.call(this.scope || this, this, state);
46187                 }
46188             }
46189         }
46190         return this;
46191     },
46192
46193     // private
46194     onDisable : function(){
46195         this.onDisableChange(true);
46196     },
46197
46198     // private
46199     onEnable : function(){
46200         this.onDisableChange(false);
46201     },
46202
46203     onDisableChange : function(disabled){
46204         if(this.el){
46205             if(!Ext.isIE6 || !this.text){
46206                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
46207             }
46208             this.el.dom.disabled = disabled;
46209         }
46210         this.disabled = disabled;
46211     },
46212
46213     /**
46214      * Show this button's menu (if it has one)
46215      */
46216     showMenu : function(){
46217         if(this.rendered && this.menu){
46218             if(this.tooltip){
46219                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
46220             }
46221             if(this.menu.isVisible()){
46222                 this.menu.hide();
46223             }
46224             this.menu.ownerCt = this;
46225             this.menu.show(this.el, this.menuAlign);
46226         }
46227         return this;
46228     },
46229
46230     /**
46231      * Hide this button's menu (if it has one)
46232      */
46233     hideMenu : function(){
46234         if(this.hasVisibleMenu()){
46235             this.menu.hide();
46236         }
46237         return this;
46238     },
46239
46240     /**
46241      * Returns true if the button has a menu and it is visible
46242      * @return {Boolean}
46243      */
46244     hasVisibleMenu : function(){
46245         return this.menu && this.menu.ownerCt == this && this.menu.isVisible();
46246     },
46247
46248     // private
46249     onClick : function(e){
46250         if(e){
46251             e.preventDefault();
46252         }
46253         if(e.button !== 0){
46254             return;
46255         }
46256         if(!this.disabled){
46257             if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){
46258                 this.toggle();
46259             }
46260             if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){
46261                 this.showMenu();
46262             }
46263             this.fireEvent('click', this, e);
46264             if(this.handler){
46265                 //this.el.removeClass('x-btn-over');
46266                 this.handler.call(this.scope || this, this, e);
46267             }
46268         }
46269     },
46270
46271     // private
46272     isMenuTriggerOver : function(e, internal){
46273         return this.menu && !internal;
46274     },
46275
46276     // private
46277     isMenuTriggerOut : function(e, internal){
46278         return this.menu && !internal;
46279     },
46280
46281     // private
46282     onMouseOver : function(e){
46283         if(!this.disabled){
46284             var internal = e.within(this.el,  true);
46285             if(!internal){
46286                 this.el.addClass('x-btn-over');
46287                 if(!this.monitoringMouseOver){
46288                     this.doc.on('mouseover', this.monitorMouseOver, this);
46289                     this.monitoringMouseOver = true;
46290                 }
46291                 this.fireEvent('mouseover', this, e);
46292             }
46293             if(this.isMenuTriggerOver(e, internal)){
46294                 this.fireEvent('menutriggerover', this, this.menu, e);
46295             }
46296         }
46297     },
46298
46299     // private
46300     monitorMouseOver : function(e){
46301         if(e.target != this.el.dom && !e.within(this.el)){
46302             if(this.monitoringMouseOver){
46303                 this.doc.un('mouseover', this.monitorMouseOver, this);
46304                 this.monitoringMouseOver = false;
46305             }
46306             this.onMouseOut(e);
46307         }
46308     },
46309
46310     // private
46311     onMouseOut : function(e){
46312         var internal = e.within(this.el) && e.target != this.el.dom;
46313         this.el.removeClass('x-btn-over');
46314         this.fireEvent('mouseout', this, e);
46315         if(this.isMenuTriggerOut(e, internal)){
46316             this.fireEvent('menutriggerout', this, this.menu, e);
46317         }
46318     },
46319
46320     focus : function() {
46321         this.btnEl.focus();
46322     },
46323
46324     blur : function() {
46325         this.btnEl.blur();
46326     },
46327
46328     // private
46329     onFocus : function(e){
46330         if(!this.disabled){
46331             this.el.addClass('x-btn-focus');
46332         }
46333     },
46334     // private
46335     onBlur : function(e){
46336         this.el.removeClass('x-btn-focus');
46337     },
46338
46339     // private
46340     getClickEl : function(e, isUp){
46341        return this.el;
46342     },
46343
46344     // private
46345     onMouseDown : function(e){
46346         if(!this.disabled && e.button === 0){
46347             this.getClickEl(e).addClass('x-btn-click');
46348             this.doc.on('mouseup', this.onMouseUp, this);
46349         }
46350     },
46351     // private
46352     onMouseUp : function(e){
46353         if(e.button === 0){
46354             this.getClickEl(e, true).removeClass('x-btn-click');
46355             this.doc.un('mouseup', this.onMouseUp, this);
46356         }
46357     },
46358     // private
46359     onMenuShow : function(e){
46360         if(this.menu.ownerCt == this){
46361             this.menu.ownerCt = this;
46362             this.ignoreNextClick = 0;
46363             this.el.addClass('x-btn-menu-active');
46364             this.fireEvent('menushow', this, this.menu);
46365         }
46366     },
46367     // private
46368     onMenuHide : function(e){
46369         if(this.menu.ownerCt == this){
46370             this.el.removeClass('x-btn-menu-active');
46371             this.ignoreNextClick = this.restoreClick.defer(250, this);
46372             this.fireEvent('menuhide', this, this.menu);
46373             delete this.menu.ownerCt;
46374         }
46375     },
46376
46377     // private
46378     restoreClick : function(){
46379         this.ignoreNextClick = 0;
46380     }
46381
46382     /**
46383      * @cfg {String} autoEl @hide
46384      */
46385     /**
46386      * @cfg {String/Object} html @hide
46387      */
46388     /**
46389      * @cfg {String} contentEl  @hide
46390      */
46391     /**
46392      * @cfg {Mixed} data  @hide
46393      */
46394     /**
46395      * @cfg {Mixed} tpl  @hide
46396      */
46397     /**
46398      * @cfg {String} tplWriteMode  @hide
46399      */
46400 });
46401 Ext.reg('button', Ext.Button);
46402
46403 // Private utility class used by Button
46404 Ext.ButtonToggleMgr = function(){
46405    var groups = {};
46406
46407    function toggleGroup(btn, state){
46408        if(state){
46409            var g = groups[btn.toggleGroup];
46410            for(var i = 0, l = g.length; i < l; i++){
46411                if(g[i] != btn){
46412                    g[i].toggle(false);
46413                }
46414            }
46415        }
46416    }
46417
46418    return {
46419        register : function(btn){
46420            if(!btn.toggleGroup){
46421                return;
46422            }
46423            var g = groups[btn.toggleGroup];
46424            if(!g){
46425                g = groups[btn.toggleGroup] = [];
46426            }
46427            g.push(btn);
46428            btn.on('toggle', toggleGroup);
46429        },
46430
46431        unregister : function(btn){
46432            if(!btn.toggleGroup){
46433                return;
46434            }
46435            var g = groups[btn.toggleGroup];
46436            if(g){
46437                g.remove(btn);
46438                btn.un('toggle', toggleGroup);
46439            }
46440        },
46441
46442        /**
46443         * Gets the pressed button in the passed group or null
46444         * @param {String} group
46445         * @return Button
46446         */
46447        getPressed : function(group){
46448            var g = groups[group];
46449            if(g){
46450                for(var i = 0, len = g.length; i < len; i++){
46451                    if(g[i].pressed === true){
46452                        return g[i];
46453                    }
46454                }
46455            }
46456            return null;
46457        }
46458    };
46459 }();
46460 /**
46461  * @class Ext.SplitButton
46462  * @extends Ext.Button
46463  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
46464  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
46465  * options to the primary button action, but any custom handler can provide the arrowclick implementation.  Example usage:
46466  * <pre><code>
46467 // display a dropdown menu:
46468 new Ext.SplitButton({
46469         renderTo: 'button-ct', // the container id
46470         text: 'Options',
46471         handler: optionsHandler, // handle a click on the button itself
46472         menu: new Ext.menu.Menu({
46473         items: [
46474                 // these items will render as dropdown menu items when the arrow is clicked:
46475                 {text: 'Item 1', handler: item1Handler},
46476                 {text: 'Item 2', handler: item2Handler}
46477         ]
46478         })
46479 });
46480
46481 // Instead of showing a menu, you provide any type of custom
46482 // functionality you want when the dropdown arrow is clicked:
46483 new Ext.SplitButton({
46484         renderTo: 'button-ct',
46485         text: 'Options',
46486         handler: optionsHandler,
46487         arrowHandler: myCustomHandler
46488 });
46489 </code></pre>
46490  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
46491  * @cfg {String} arrowTooltip The title attribute of the arrow
46492  * @constructor
46493  * Create a new menu button
46494  * @param {Object} config The config object
46495  * @xtype splitbutton
46496  */
46497 Ext.SplitButton = Ext.extend(Ext.Button, {
46498         // private
46499     arrowSelector : 'em',
46500     split: true,
46501
46502     // private
46503     initComponent : function(){
46504         Ext.SplitButton.superclass.initComponent.call(this);
46505         /**
46506          * @event arrowclick
46507          * Fires when this button's arrow is clicked
46508          * @param {MenuButton} this
46509          * @param {EventObject} e The click event
46510          */
46511         this.addEvents("arrowclick");
46512     },
46513
46514     // private
46515     onRender : function(){
46516         Ext.SplitButton.superclass.onRender.apply(this, arguments);
46517         if(this.arrowTooltip){
46518             this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;
46519         }
46520     },
46521
46522     /**
46523      * Sets this button's arrow click handler.
46524      * @param {Function} handler The function to call when the arrow is clicked
46525      * @param {Object} scope (optional) Scope for the function passed above
46526      */
46527     setArrowHandler : function(handler, scope){
46528         this.arrowHandler = handler;
46529         this.scope = scope;
46530     },
46531
46532     getMenuClass : function(){
46533         return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');
46534     },
46535
46536     isClickOnArrow : function(e){
46537         if (this.arrowAlign != 'bottom') {
46538             var visBtn = this.el.child('em.x-btn-split');
46539             var right = visBtn.getRegion().right - visBtn.getPadding('r');
46540             return e.getPageX() > right;
46541         } else {
46542             return e.getPageY() > this.btnEl.getRegion().bottom;
46543         }
46544     },
46545
46546     // private
46547     onClick : function(e, t){
46548         e.preventDefault();
46549         if(!this.disabled){
46550             if(this.isClickOnArrow(e)){
46551                 if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
46552                     this.showMenu();
46553                 }
46554                 this.fireEvent("arrowclick", this, e);
46555                 if(this.arrowHandler){
46556                     this.arrowHandler.call(this.scope || this, this, e);
46557                 }
46558             }else{
46559                 if(this.enableToggle){
46560                     this.toggle();
46561                 }
46562                 this.fireEvent("click", this, e);
46563                 if(this.handler){
46564                     this.handler.call(this.scope || this, this, e);
46565                 }
46566             }
46567         }
46568     },
46569
46570     // private
46571     isMenuTriggerOver : function(e){
46572         return this.menu && e.target.tagName == this.arrowSelector;
46573     },
46574
46575     // private
46576     isMenuTriggerOut : function(e, internal){
46577         return this.menu && e.target.tagName != this.arrowSelector;
46578     }
46579 });
46580
46581 Ext.reg('splitbutton', Ext.SplitButton);/**
46582  * @class Ext.CycleButton
46583  * @extends Ext.SplitButton
46584  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically
46585  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's
46586  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the
46587  * button displays the dropdown menu just like a normal SplitButton.  Example usage:
46588  * <pre><code>
46589 var btn = new Ext.CycleButton({
46590     showText: true,
46591     prependText: 'View as ',
46592     items: [{
46593         text:'text only',
46594         iconCls:'view-text',
46595         checked:true
46596     },{
46597         text:'HTML',
46598         iconCls:'view-html'
46599     }],
46600     changeHandler:function(btn, item){
46601         Ext.Msg.alert('Change View', item.text);
46602     }
46603 });
46604 </code></pre>
46605  * @constructor
46606  * Create a new split button
46607  * @param {Object} config The config object
46608  * @xtype cycle
46609  */
46610 Ext.CycleButton = Ext.extend(Ext.SplitButton, {
46611     /**
46612      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the
46613      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})
46614      */
46615     /**
46616      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)
46617      */
46618     /**
46619      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the
46620      * button's text (only applies when showText = true, defaults to '')
46621      */
46622     /**
46623      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu
46624      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead
46625      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the
46626      * following argument list: (SplitButton this, Ext.menu.CheckItem item)
46627      */
46628     /**
46629      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This
46630      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the 
46631      * default behavior of changing the button's icon to match the selected item's icon on change.
46632      */
46633     /**
46634      * @property menu
46635      * @type Menu
46636      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.
46637      */
46638
46639     // private
46640     getItemText : function(item){
46641         if(item && this.showText === true){
46642             var text = '';
46643             if(this.prependText){
46644                 text += this.prependText;
46645             }
46646             text += item.text;
46647             return text;
46648         }
46649         return undefined;
46650     },
46651
46652     /**
46653      * Sets the button's active menu item.
46654      * @param {Ext.menu.CheckItem} item The item to activate
46655      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)
46656      */
46657     setActiveItem : function(item, suppressEvent){
46658         if(!Ext.isObject(item)){
46659             item = this.menu.getComponent(item);
46660         }
46661         if(item){
46662             if(!this.rendered){
46663                 this.text = this.getItemText(item);
46664                 this.iconCls = item.iconCls;
46665             }else{
46666                 var t = this.getItemText(item);
46667                 if(t){
46668                     this.setText(t);
46669                 }
46670                 this.setIconClass(item.iconCls);
46671             }
46672             this.activeItem = item;
46673             if(!item.checked){
46674                 item.setChecked(true, false);
46675             }
46676             if(this.forceIcon){
46677                 this.setIconClass(this.forceIcon);
46678             }
46679             if(!suppressEvent){
46680                 this.fireEvent('change', this, item);
46681             }
46682         }
46683     },
46684
46685     /**
46686      * Gets the currently active menu item.
46687      * @return {Ext.menu.CheckItem} The active item
46688      */
46689     getActiveItem : function(){
46690         return this.activeItem;
46691     },
46692
46693     // private
46694     initComponent : function(){
46695         this.addEvents(
46696             /**
46697              * @event change
46698              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function
46699              * is set on this CycleButton, it will be called instead on active item change and this change event will
46700              * not be fired.
46701              * @param {Ext.CycleButton} this
46702              * @param {Ext.menu.CheckItem} item The menu item that was selected
46703              */
46704             "change"
46705         );
46706
46707         if(this.changeHandler){
46708             this.on('change', this.changeHandler, this.scope||this);
46709             delete this.changeHandler;
46710         }
46711
46712         this.itemCount = this.items.length;
46713
46714         this.menu = {cls:'x-cycle-menu', items:[]};
46715         var checked = 0;
46716         Ext.each(this.items, function(item, i){
46717             Ext.apply(item, {
46718                 group: item.group || this.id,
46719                 itemIndex: i,
46720                 checkHandler: this.checkHandler,
46721                 scope: this,
46722                 checked: item.checked || false
46723             });
46724             this.menu.items.push(item);
46725             if(item.checked){
46726                 checked = i;
46727             }
46728         }, this);
46729         Ext.CycleButton.superclass.initComponent.call(this);
46730         this.on('click', this.toggleSelected, this);
46731         this.setActiveItem(checked, true);
46732     },
46733
46734     // private
46735     checkHandler : function(item, pressed){
46736         if(pressed){
46737             this.setActiveItem(item);
46738         }
46739     },
46740
46741     /**
46742      * This is normally called internally on button click, but can be called externally to advance the button's
46743      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu
46744      * the active item will be set to the first item in the menu.
46745      */
46746     toggleSelected : function(){
46747         var m = this.menu;
46748         m.render();
46749         // layout if we haven't before so the items are active
46750         if(!m.hasLayout){
46751             m.doLayout();
46752         }
46753         
46754         var nextIdx, checkItem;
46755         for (var i = 1; i < this.itemCount; i++) {
46756             nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;
46757             // check the potential item
46758             checkItem = m.items.itemAt(nextIdx);
46759             // if its not disabled then check it.
46760             if (!checkItem.disabled) {
46761                 checkItem.setChecked(true);
46762                 break;
46763             }
46764         }
46765     }
46766 });
46767 Ext.reg('cycle', Ext.CycleButton);/**
46768  * @class Ext.Toolbar
46769  * @extends Ext.Container
46770  * <p>Basic Toolbar class. Although the <tt>{@link Ext.Container#defaultType defaultType}</tt> for Toolbar
46771  * is <tt>{@link Ext.Button button}</tt>, Toolbar elements (child items for the Toolbar container) may
46772  * be virtually any type of Component. Toolbar elements can be created explicitly via their constructors,
46773  * or implicitly via their xtypes, and can be <tt>{@link #add}</tt>ed dynamically.</p>
46774  * <p>Some items have shortcut strings for creation:</p>
46775  * <pre>
46776 <u>Shortcut</u>  <u>xtype</u>          <u>Class</u>                  <u>Description</u>
46777 '->'      'tbfill'       {@link Ext.Toolbar.Fill}       begin using the right-justified button container
46778 '-'       'tbseparator'  {@link Ext.Toolbar.Separator}  add a vertical separator bar between toolbar items
46779 ' '       'tbspacer'     {@link Ext.Toolbar.Spacer}     add horiztonal space between elements
46780  * </pre>
46781  *
46782  * Example usage of various elements:
46783  * <pre><code>
46784 var tb = new Ext.Toolbar({
46785     renderTo: document.body,
46786     width: 600,
46787     height: 100,
46788     items: [
46789         {
46790             // xtype: 'button', // default for Toolbars, same as 'tbbutton'
46791             text: 'Button'
46792         },
46793         {
46794             xtype: 'splitbutton', // same as 'tbsplitbutton'
46795             text: 'Split Button'
46796         },
46797         // begin using the right-justified button container
46798         '->', // same as {xtype: 'tbfill'}, // Ext.Toolbar.Fill
46799         {
46800             xtype: 'textfield',
46801             name: 'field1',
46802             emptyText: 'enter search term'
46803         },
46804         // add a vertical separator bar between toolbar items
46805         '-', // same as {xtype: 'tbseparator'} to create Ext.Toolbar.Separator
46806         'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.Toolbar.TextItem
46807         {xtype: 'tbspacer'},// same as ' ' to create Ext.Toolbar.Spacer
46808         'text 2',
46809         {xtype: 'tbspacer', width: 50}, // add a 50px space
46810         'text 3'
46811     ]
46812 });
46813  * </code></pre>
46814  * Example adding a ComboBox within a menu of a button:
46815  * <pre><code>
46816 // ComboBox creation
46817 var combo = new Ext.form.ComboBox({
46818     store: new Ext.data.ArrayStore({
46819         autoDestroy: true,
46820         fields: ['initials', 'fullname'],
46821         data : [
46822             ['FF', 'Fred Flintstone'],
46823             ['BR', 'Barney Rubble']
46824         ]
46825     }),
46826     displayField: 'fullname',
46827     typeAhead: true,
46828     mode: 'local',
46829     forceSelection: true,
46830     triggerAction: 'all',
46831     emptyText: 'Select a name...',
46832     selectOnFocus: true,
46833     width: 135,
46834     getListParent: function() {
46835         return this.el.up('.x-menu');
46836     },
46837     iconCls: 'no-icon' //use iconCls if placing within menu to shift to right side of menu
46838 });
46839
46840 // put ComboBox in a Menu
46841 var menu = new Ext.menu.Menu({
46842     id: 'mainMenu',
46843     items: [
46844         combo // A Field in a Menu
46845     ]
46846 });
46847
46848 // add a Button with the menu
46849 tb.add({
46850         text:'Button w/ Menu',
46851         menu: menu  // assign menu by instance
46852     });
46853 tb.doLayout();
46854  * </code></pre>
46855  * @constructor
46856  * Creates a new Toolbar
46857  * @param {Object/Array} config A config object or an array of buttons to <tt>{@link #add}</tt>
46858  * @xtype toolbar
46859  */
46860 Ext.Toolbar = function(config){
46861     if(Ext.isArray(config)){
46862         config = {items: config, layout: 'toolbar'};
46863     } else {
46864         config = Ext.apply({
46865             layout: 'toolbar'
46866         }, config);
46867         if(config.buttons) {
46868             config.items = config.buttons;
46869         }
46870     }
46871     Ext.Toolbar.superclass.constructor.call(this, config);
46872 };
46873
46874 (function(){
46875
46876 var T = Ext.Toolbar;
46877
46878 Ext.extend(T, Ext.Container, {
46879
46880     defaultType: 'button',
46881
46882     /**
46883      * @cfg {String/Object} layout
46884      * This class assigns a default layout (<code>layout:'<b>toolbar</b>'</code>).
46885      * Developers <i>may</i> override this configuration option if another layout
46886      * is required (the constructor must be passed a configuration object in this
46887      * case instead of an array).
46888      * See {@link Ext.Container#layout} for additional information.
46889      */
46890
46891     enableOverflow : false,
46892
46893     /**
46894      * @cfg {Boolean} enableOverflow
46895      * Defaults to false. Configure <code>true<code> to make the toolbar provide a button
46896      * which activates a dropdown Menu to show items which overflow the Toolbar's width.
46897      */
46898     /**
46899      * @cfg {String} buttonAlign
46900      * <p>The default position at which to align child items. Defaults to <code>"left"</code></p>
46901      * <p>May be specified as <code>"center"</code> to cause items added before a Fill (A <code>"->"</code>) item
46902      * to be centered in the Toolbar. Items added after a Fill are still right-aligned.</p>
46903      * <p>Specify as <code>"right"</code> to right align all child items.</p>
46904      */
46905
46906     trackMenus : true,
46907     internalDefaults: {removeMode: 'container', hideParent: true},
46908     toolbarCls: 'x-toolbar',
46909
46910     initComponent : function(){
46911         T.superclass.initComponent.call(this);
46912
46913         /**
46914          * @event overflowchange
46915          * Fires after the overflow state has changed.
46916          * @param {Object} c The Container
46917          * @param {Boolean} lastOverflow overflow state
46918          */
46919         this.addEvents('overflowchange');
46920     },
46921
46922     // private
46923     onRender : function(ct, position){
46924         if(!this.el){
46925             if(!this.autoCreate){
46926                 this.autoCreate = {
46927                     cls: this.toolbarCls + ' x-small-editor'
46928                 };
46929             }
46930             this.el = ct.createChild(Ext.apply({ id: this.id },this.autoCreate), position);
46931             Ext.Toolbar.superclass.onRender.apply(this, arguments);
46932         }
46933     },
46934
46935     /**
46936      * <p>Adds element(s) to the toolbar -- this function takes a variable number of
46937      * arguments of mixed type and adds them to the toolbar.</p>
46938      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
46939      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
46940      * <ul>
46941      * <li>{@link Ext.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
46942      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
46943      * <li>Field: Any form field (equivalent to {@link #addField})</li>
46944      * <li>Item: Any subclass of {@link Ext.Toolbar.Item} (equivalent to {@link #addItem})</li>
46945      * <li>String: Any generic string (gets wrapped in a {@link Ext.Toolbar.TextItem}, equivalent to {@link #addText}).
46946      * Note that there are a few special strings that are treated differently as explained next.</li>
46947      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>
46948      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
46949      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
46950      * </ul>
46951      * @param {Mixed} arg2
46952      * @param {Mixed} etc.
46953      * @method add
46954      */
46955
46956     // private
46957     lookupComponent : function(c){
46958         if(Ext.isString(c)){
46959             if(c == '-'){
46960                 c = new T.Separator();
46961             }else if(c == ' '){
46962                 c = new T.Spacer();
46963             }else if(c == '->'){
46964                 c = new T.Fill();
46965             }else{
46966                 c = new T.TextItem(c);
46967             }
46968             this.applyDefaults(c);
46969         }else{
46970             if(c.isFormField || c.render){ // some kind of form field, some kind of Toolbar.Item
46971                 c = this.createComponent(c);
46972             }else if(c.tag){ // DomHelper spec
46973                 c = new T.Item({autoEl: c});
46974             }else if(c.tagName){ // element
46975                 c = new T.Item({el:c});
46976             }else if(Ext.isObject(c)){ // must be button config?
46977                 c = c.xtype ? this.createComponent(c) : this.constructButton(c);
46978             }
46979         }
46980         return c;
46981     },
46982
46983     // private
46984     applyDefaults : function(c){
46985         if(!Ext.isString(c)){
46986             c = Ext.Toolbar.superclass.applyDefaults.call(this, c);
46987             var d = this.internalDefaults;
46988             if(c.events){
46989                 Ext.applyIf(c.initialConfig, d);
46990                 Ext.apply(c, d);
46991             }else{
46992                 Ext.applyIf(c, d);
46993             }
46994         }
46995         return c;
46996     },
46997
46998     /**
46999      * Adds a separator
47000      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47001      * @return {Ext.Toolbar.Item} The separator {@link Ext.Toolbar.Item item}
47002      */
47003     addSeparator : function(){
47004         return this.add(new T.Separator());
47005     },
47006
47007     /**
47008      * Adds a spacer element
47009      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47010      * @return {Ext.Toolbar.Spacer} The spacer item
47011      */
47012     addSpacer : function(){
47013         return this.add(new T.Spacer());
47014     },
47015
47016     /**
47017      * Forces subsequent additions into the float:right toolbar
47018      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47019      */
47020     addFill : function(){
47021         this.add(new T.Fill());
47022     },
47023
47024     /**
47025      * Adds any standard HTML element to the toolbar
47026      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47027      * @param {Mixed} el The element or id of the element to add
47028      * @return {Ext.Toolbar.Item} The element's item
47029      */
47030     addElement : function(el){
47031         return this.addItem(new T.Item({el:el}));
47032     },
47033
47034     /**
47035      * Adds any Toolbar.Item or subclass
47036      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47037      * @param {Ext.Toolbar.Item} item
47038      * @return {Ext.Toolbar.Item} The item
47039      */
47040     addItem : function(item){
47041         return this.add.apply(this, arguments);
47042     },
47043
47044     /**
47045      * Adds a button (or buttons). See {@link Ext.Button} for more info on the config.
47046      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47047      * @param {Object/Array} config A button config or array of configs
47048      * @return {Ext.Button/Array}
47049      */
47050     addButton : function(config){
47051         if(Ext.isArray(config)){
47052             var buttons = [];
47053             for(var i = 0, len = config.length; i < len; i++) {
47054                 buttons.push(this.addButton(config[i]));
47055             }
47056             return buttons;
47057         }
47058         return this.add(this.constructButton(config));
47059     },
47060
47061     /**
47062      * Adds text to the toolbar
47063      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47064      * @param {String} text The text to add
47065      * @return {Ext.Toolbar.Item} The element's item
47066      */
47067     addText : function(text){
47068         return this.addItem(new T.TextItem(text));
47069     },
47070
47071     /**
47072      * Adds a new element to the toolbar from the passed {@link Ext.DomHelper} config
47073      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47074      * @param {Object} config
47075      * @return {Ext.Toolbar.Item} The element's item
47076      */
47077     addDom : function(config){
47078         return this.add(new T.Item({autoEl: config}));
47079     },
47080
47081     /**
47082      * Adds a dynamically rendered Ext.form field (TextField, ComboBox, etc). Note: the field should not have
47083      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
47084      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47085      * @param {Ext.form.Field} field
47086      * @return {Ext.Toolbar.Item}
47087      */
47088     addField : function(field){
47089         return this.add(field);
47090     },
47091
47092     /**
47093      * Inserts any {@link Ext.Toolbar.Item}/{@link Ext.Button} at the specified index.
47094      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47095      * @param {Number} index The index where the item is to be inserted
47096      * @param {Object/Ext.Toolbar.Item/Ext.Button/Array} item The button, or button config object to be
47097      * inserted, or an array of buttons/configs.
47098      * @return {Ext.Button/Item}
47099      */
47100     insertButton : function(index, item){
47101         if(Ext.isArray(item)){
47102             var buttons = [];
47103             for(var i = 0, len = item.length; i < len; i++) {
47104                buttons.push(this.insertButton(index + i, item[i]));
47105             }
47106             return buttons;
47107         }
47108         return Ext.Toolbar.superclass.insert.call(this, index, item);
47109     },
47110
47111     // private
47112     trackMenu : function(item, remove){
47113         if(this.trackMenus && item.menu){
47114             var method = remove ? 'mun' : 'mon';
47115             this[method](item, 'menutriggerover', this.onButtonTriggerOver, this);
47116             this[method](item, 'menushow', this.onButtonMenuShow, this);
47117             this[method](item, 'menuhide', this.onButtonMenuHide, this);
47118         }
47119     },
47120
47121     // private
47122     constructButton : function(item){
47123         var b = item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
47124         return b;
47125     },
47126
47127     // private
47128     onAdd : function(c){
47129         Ext.Toolbar.superclass.onAdd.call(this);
47130         this.trackMenu(c);
47131         if(this.disabled){
47132             c.disable();
47133         }
47134     },
47135
47136     // private
47137     onRemove : function(c){
47138         Ext.Toolbar.superclass.onRemove.call(this);
47139         this.trackMenu(c, true);
47140     },
47141
47142     // private
47143     onDisable : function(){
47144         this.items.each(function(item){
47145              if(item.disable){
47146                  item.disable();
47147              }
47148         });
47149     },
47150
47151     // private
47152     onEnable : function(){
47153         this.items.each(function(item){
47154              if(item.enable){
47155                  item.enable();
47156              }
47157         });
47158     },
47159
47160     // private
47161     onButtonTriggerOver : function(btn){
47162         if(this.activeMenuBtn && this.activeMenuBtn != btn){
47163             this.activeMenuBtn.hideMenu();
47164             btn.showMenu();
47165             this.activeMenuBtn = btn;
47166         }
47167     },
47168
47169     // private
47170     onButtonMenuShow : function(btn){
47171         this.activeMenuBtn = btn;
47172     },
47173
47174     // private
47175     onButtonMenuHide : function(btn){
47176         delete this.activeMenuBtn;
47177     }
47178 });
47179 Ext.reg('toolbar', Ext.Toolbar);
47180
47181 /**
47182  * @class Ext.Toolbar.Item
47183  * @extends Ext.BoxComponent
47184  * The base class that other non-interacting Toolbar Item classes should extend in order to
47185  * get some basic common toolbar item functionality.
47186  * @constructor
47187  * Creates a new Item
47188  * @param {HTMLElement} el
47189  * @xtype tbitem
47190  */
47191 T.Item = Ext.extend(Ext.BoxComponent, {
47192     hideParent: true, //  Hiding a Toolbar.Item hides its containing TD
47193     enable:Ext.emptyFn,
47194     disable:Ext.emptyFn,
47195     focus:Ext.emptyFn
47196     /**
47197      * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.
47198      */
47199 });
47200 Ext.reg('tbitem', T.Item);
47201
47202 /**
47203  * @class Ext.Toolbar.Separator
47204  * @extends Ext.Toolbar.Item
47205  * A simple class that adds a vertical separator bar between toolbar items
47206  * (css class:<tt>'xtb-sep'</tt>). Example usage:
47207  * <pre><code>
47208 new Ext.Panel({
47209     tbar : [
47210         'Item 1',
47211         {xtype: 'tbseparator'}, // or '-'
47212         'Item 2'
47213     ]
47214 });
47215 </code></pre>
47216  * @constructor
47217  * Creates a new Separator
47218  * @xtype tbseparator
47219  */
47220 T.Separator = Ext.extend(T.Item, {
47221     onRender : function(ct, position){
47222         this.el = ct.createChild({tag:'span', cls:'xtb-sep'}, position);
47223     }
47224 });
47225 Ext.reg('tbseparator', T.Separator);
47226
47227 /**
47228  * @class Ext.Toolbar.Spacer
47229  * @extends Ext.Toolbar.Item
47230  * A simple element that adds extra horizontal space between items in a toolbar.
47231  * By default a 2px wide space is added via css specification:<pre><code>
47232 .x-toolbar .xtb-spacer {
47233     width:2px;
47234 }
47235  * </code></pre>
47236  * <p>Example usage:</p>
47237  * <pre><code>
47238 new Ext.Panel({
47239     tbar : [
47240         'Item 1',
47241         {xtype: 'tbspacer'}, // or ' '
47242         'Item 2',
47243         // space width is also configurable via javascript
47244         {xtype: 'tbspacer', width: 50}, // add a 50px space
47245         'Item 3'
47246     ]
47247 });
47248 </code></pre>
47249  * @constructor
47250  * Creates a new Spacer
47251  * @xtype tbspacer
47252  */
47253 T.Spacer = Ext.extend(T.Item, {
47254     /**
47255      * @cfg {Number} width
47256      * The width of the spacer in pixels (defaults to 2px via css style <tt>.x-toolbar .xtb-spacer</tt>).
47257      */
47258
47259     onRender : function(ct, position){
47260         this.el = ct.createChild({tag:'div', cls:'xtb-spacer', style: this.width?'width:'+this.width+'px':''}, position);
47261     }
47262 });
47263 Ext.reg('tbspacer', T.Spacer);
47264
47265 /**
47266  * @class Ext.Toolbar.Fill
47267  * @extends Ext.Toolbar.Spacer
47268  * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using
47269  * the right-justified button container.
47270  * <pre><code>
47271 new Ext.Panel({
47272     tbar : [
47273         'Item 1',
47274         {xtype: 'tbfill'}, // or '->'
47275         'Item 2'
47276     ]
47277 });
47278 </code></pre>
47279  * @constructor
47280  * Creates a new Fill
47281  * @xtype tbfill
47282  */
47283 T.Fill = Ext.extend(T.Item, {
47284     // private
47285     render : Ext.emptyFn,
47286     isFill : true
47287 });
47288 Ext.reg('tbfill', T.Fill);
47289
47290 /**
47291  * @class Ext.Toolbar.TextItem
47292  * @extends Ext.Toolbar.Item
47293  * A simple class that renders text directly into a toolbar
47294  * (with css class:<tt>'xtb-text'</tt>). Example usage:
47295  * <pre><code>
47296 new Ext.Panel({
47297     tbar : [
47298         {xtype: 'tbtext', text: 'Item 1'} // or simply 'Item 1'
47299     ]
47300 });
47301 </code></pre>
47302  * @constructor
47303  * Creates a new TextItem
47304  * @param {String/Object} text A text string, or a config object containing a <tt>text</tt> property
47305  * @xtype tbtext
47306  */
47307 T.TextItem = Ext.extend(T.Item, {
47308     /**
47309      * @cfg {String} text The text to be used as innerHTML (html tags are accepted)
47310      */
47311
47312     constructor: function(config){
47313         T.TextItem.superclass.constructor.call(this, Ext.isString(config) ? {text: config} : config);
47314     },
47315
47316     // private
47317     onRender : function(ct, position) {
47318         this.autoEl = {cls: 'xtb-text', html: this.text || ''};
47319         T.TextItem.superclass.onRender.call(this, ct, position);
47320     },
47321
47322     /**
47323      * Updates this item's text, setting the text to be used as innerHTML.
47324      * @param {String} t The text to display (html accepted).
47325      */
47326     setText : function(t) {
47327         if(this.rendered){
47328             this.el.update(t);
47329         }else{
47330             this.text = t;
47331         }
47332     }
47333 });
47334 Ext.reg('tbtext', T.TextItem);
47335
47336 // backwards compat
47337 T.Button = Ext.extend(Ext.Button, {});
47338 T.SplitButton = Ext.extend(Ext.SplitButton, {});
47339 Ext.reg('tbbutton', T.Button);
47340 Ext.reg('tbsplit', T.SplitButton);
47341
47342 })();
47343 /**
47344  * @class Ext.ButtonGroup
47345  * @extends Ext.Panel
47346  * Container for a group of buttons. Example usage:
47347  * <pre><code>
47348 var p = new Ext.Panel({
47349     title: 'Panel with Button Group',
47350     width: 300,
47351     height:200,
47352     renderTo: document.body,
47353     html: 'whatever',
47354     tbar: [{
47355         xtype: 'buttongroup',
47356         {@link #columns}: 3,
47357         title: 'Clipboard',
47358         items: [{
47359             text: 'Paste',
47360             scale: 'large',
47361             rowspan: 3, iconCls: 'add',
47362             iconAlign: 'top',
47363             cls: 'x-btn-as-arrow'
47364         },{
47365             xtype:'splitbutton',
47366             text: 'Menu Button',
47367             scale: 'large',
47368             rowspan: 3,
47369             iconCls: 'add',
47370             iconAlign: 'top',
47371             arrowAlign:'bottom',
47372             menu: [{text: 'Menu Item 1'}]
47373         },{
47374             xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]
47375         },{
47376             text: 'Copy', iconCls: 'add16'
47377         },{
47378             text: 'Format', iconCls: 'add16'
47379         }]
47380     }]
47381 });
47382  * </code></pre>
47383  * @constructor
47384  * Create a new ButtonGroup.
47385  * @param {Object} config The config object
47386  * @xtype buttongroup
47387  */
47388 Ext.ButtonGroup = Ext.extend(Ext.Panel, {
47389     /**
47390      * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the
47391      * {@link #layout configured layout manager}. See {@link Ext.layout.TableLayout#columns}.
47392      */
47393     /**
47394      * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.Panel#baseCls}.
47395      */
47396     baseCls: 'x-btn-group',
47397     /**
47398      * @cfg {String} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.Container#layout}.
47399      */
47400     layout:'table',
47401     defaultType: 'button',
47402     /**
47403      * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.Panel#frame}.
47404      */
47405     frame: true,
47406     internalDefaults: {removeMode: 'container', hideParent: true},
47407
47408     initComponent : function(){
47409         this.layoutConfig = this.layoutConfig || {};
47410         Ext.applyIf(this.layoutConfig, {
47411             columns : this.columns
47412         });
47413         if(!this.title){
47414             this.addClass('x-btn-group-notitle');
47415         }
47416         this.on('afterlayout', this.onAfterLayout, this);
47417         Ext.ButtonGroup.superclass.initComponent.call(this);
47418     },
47419
47420     applyDefaults : function(c){
47421         c = Ext.ButtonGroup.superclass.applyDefaults.call(this, c);
47422         var d = this.internalDefaults;
47423         if(c.events){
47424             Ext.applyIf(c.initialConfig, d);
47425             Ext.apply(c, d);
47426         }else{
47427             Ext.applyIf(c, d);
47428         }
47429         return c;
47430     },
47431
47432     onAfterLayout : function(){
47433         var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;
47434         this.body.setWidth(bodyWidth);
47435         this.el.setWidth(bodyWidth + this.getFrameWidth());
47436     }
47437     /**
47438      * @cfg {Array} tools  @hide
47439      */
47440 });
47441
47442 Ext.reg('buttongroup', Ext.ButtonGroup);
47443 /**
47444  * @class Ext.PagingToolbar
47445  * @extends Ext.Toolbar
47446  * <p>As the amount of records increases, the time required for the browser to render
47447  * them increases. Paging is used to reduce the amount of data exchanged with the client.
47448  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
47449  * scrollbars will be added.</p>
47450  * <p>Paging is typically handled on the server side (see exception below). The client sends
47451  * parameters to the server side, which the server needs to interpret and then respond with the
47452  * approprate data.</p>
47453  * <p><b>Ext.PagingToolbar</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
47454  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
47455  * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
47456  * paging criteria.</p>
47457  * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
47458  * <pre><code>
47459 Ext.QuickTips.init(); // to display button quicktips
47460
47461 var myStore = new Ext.data.Store({
47462     reader: new Ext.data.JsonReader({
47463         {@link Ext.data.JsonReader#totalProperty totalProperty}: 'results', 
47464         ...
47465     }),
47466     ...
47467 });
47468
47469 var myPageSize = 25;  // server script should only send back 25 items at a time
47470
47471 var grid = new Ext.grid.GridPanel({
47472     ...
47473     store: myStore,
47474     bbar: new Ext.PagingToolbar({
47475         {@link #store}: myStore,       // grid and PagingToolbar using same store
47476         {@link #displayInfo}: true,
47477         {@link #pageSize}: myPageSize,
47478         {@link #prependButtons}: true,
47479         items: [
47480             'text 1'
47481         ]
47482     })
47483 });
47484  * </code></pre>
47485  *
47486  * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
47487  * <pre><code>
47488 store.load({
47489     params: {
47490         // specify params for the first page load if using paging
47491         start: 0,          
47492         limit: myPageSize,
47493         // other params
47494         foo:   'bar'
47495     }
47496 });
47497  * </code></pre>
47498  * 
47499  * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
47500  * <pre><code>
47501 var myStore = new Ext.data.Store({
47502     {@link Ext.data.Store#autoLoad autoLoad}: {params:{start: 0, limit: 25}},
47503     ...
47504 });
47505  * </code></pre>
47506  * 
47507  * <p>The packet sent back from the server would have this form:</p>
47508  * <pre><code>
47509 {
47510     "success": true,
47511     "results": 2000, 
47512     "rows": [ // <b>*Note:</b> this must be an Array 
47513         { "id":  1, "name": "Bill", "occupation": "Gardener" },
47514         { "id":  2, "name":  "Ben", "occupation": "Horticulturalist" },
47515         ...
47516         { "id": 25, "name":  "Sue", "occupation": "Botanist" }
47517     ]
47518 }
47519  * </code></pre>
47520  * <p><u>Paging with Local Data</u></p>
47521  * <p>Paging can also be accomplished with local data using extensions:</p>
47522  * <div class="mdetail-params"><ul>
47523  * <li><a href="http://extjs.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
47524  * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
47525  * </ul></div>
47526  * @constructor Create a new PagingToolbar
47527  * @param {Object} config The config object
47528  * @xtype paging
47529  */
47530 (function() {
47531
47532 var T = Ext.Toolbar;
47533
47534 Ext.PagingToolbar = Ext.extend(Ext.Toolbar, {
47535     /**
47536      * @cfg {Ext.data.Store} store
47537      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
47538      */
47539     /**
47540      * @cfg {Boolean} displayInfo
47541      * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
47542      */
47543     /**
47544      * @cfg {Number} pageSize
47545      * The number of records to display per page (defaults to <tt>20</tt>)
47546      */
47547     pageSize : 20,
47548     /**
47549      * @cfg {Boolean} prependButtons
47550      * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
47551      * Defaults to <tt>false</tt>.
47552      */
47553     /**
47554      * @cfg {String} displayMsg
47555      * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
47556      * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
47557      * that are replaced by the values for start, end and total respectively. These tokens should
47558      * be preserved when overriding this string if showing those values is desired.
47559      */
47560     displayMsg : 'Displaying {0} - {1} of {2}',
47561     /**
47562      * @cfg {String} emptyMsg
47563      * The message to display when no records are found (defaults to 'No data to display')
47564      */
47565     emptyMsg : 'No data to display',
47566     /**
47567      * @cfg {String} beforePageText
47568      * The text displayed before the input item (defaults to <tt>'Page'</tt>).
47569      */
47570     beforePageText : 'Page',
47571     /**
47572      * @cfg {String} afterPageText
47573      * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
47574      * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
47575      * total pages. This token should be preserved when overriding this string if showing the
47576      * total page count is desired.
47577      */
47578     afterPageText : 'of {0}',
47579     /**
47580      * @cfg {String} firstText
47581      * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
47582      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47583      */
47584     firstText : 'First Page',
47585     /**
47586      * @cfg {String} prevText
47587      * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
47588      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47589      */
47590     prevText : 'Previous Page',
47591     /**
47592      * @cfg {String} nextText
47593      * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
47594      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47595      */
47596     nextText : 'Next Page',
47597     /**
47598      * @cfg {String} lastText
47599      * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
47600      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47601      */
47602     lastText : 'Last Page',
47603     /**
47604      * @cfg {String} refreshText
47605      * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
47606      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47607      */
47608     refreshText : 'Refresh',
47609
47610     /**
47611      * <p><b>Deprecated</b>. <code>paramNames</code> should be set in the <b>data store</b>
47612      * (see {@link Ext.data.Store#paramNames}).</p>
47613      * <br><p>Object mapping of parameter names used for load calls, initially set to:</p>
47614      * <pre>{start: 'start', limit: 'limit'}</pre>
47615      * @type Object
47616      * @property paramNames
47617      * @deprecated
47618      */
47619
47620     /**
47621      * The number of records to display per page.  See also <tt>{@link #cursor}</tt>.
47622      * @type Number
47623      * @property pageSize
47624      */
47625
47626     /**
47627      * Indicator for the record position.  This property might be used to get the active page
47628      * number for example:<pre><code>
47629      * // t is reference to the paging toolbar instance
47630      * var activePage = Math.ceil((t.cursor + t.pageSize) / t.pageSize);
47631      * </code></pre>
47632      * @type Number
47633      * @property cursor
47634      */
47635
47636     initComponent : function(){
47637         var pagingItems = [this.first = new T.Button({
47638             tooltip: this.firstText,
47639             overflowText: this.firstText,
47640             iconCls: 'x-tbar-page-first',
47641             disabled: true,
47642             handler: this.moveFirst,
47643             scope: this
47644         }), this.prev = new T.Button({
47645             tooltip: this.prevText,
47646             overflowText: this.prevText,
47647             iconCls: 'x-tbar-page-prev',
47648             disabled: true,
47649             handler: this.movePrevious,
47650             scope: this
47651         }), '-', this.beforePageText,
47652         this.inputItem = new Ext.form.NumberField({
47653             cls: 'x-tbar-page-number',
47654             allowDecimals: false,
47655             allowNegative: false,
47656             enableKeyEvents: true,
47657             selectOnFocus: true,
47658             submitValue: false,
47659             listeners: {
47660                 scope: this,
47661                 keydown: this.onPagingKeyDown,
47662                 blur: this.onPagingBlur
47663             }
47664         }), this.afterTextItem = new T.TextItem({
47665             text: String.format(this.afterPageText, 1)
47666         }), '-', this.next = new T.Button({
47667             tooltip: this.nextText,
47668             overflowText: this.nextText,
47669             iconCls: 'x-tbar-page-next',
47670             disabled: true,
47671             handler: this.moveNext,
47672             scope: this
47673         }), this.last = new T.Button({
47674             tooltip: this.lastText,
47675             overflowText: this.lastText,
47676             iconCls: 'x-tbar-page-last',
47677             disabled: true,
47678             handler: this.moveLast,
47679             scope: this
47680         }), '-', this.refresh = new T.Button({
47681             tooltip: this.refreshText,
47682             overflowText: this.refreshText,
47683             iconCls: 'x-tbar-loading',
47684             handler: this.doRefresh,
47685             scope: this
47686         })];
47687
47688
47689         var userItems = this.items || this.buttons || [];
47690         if (this.prependButtons) {
47691             this.items = userItems.concat(pagingItems);
47692         }else{
47693             this.items = pagingItems.concat(userItems);
47694         }
47695         delete this.buttons;
47696         if(this.displayInfo){
47697             this.items.push('->');
47698             this.items.push(this.displayItem = new T.TextItem({}));
47699         }
47700         Ext.PagingToolbar.superclass.initComponent.call(this);
47701         this.addEvents(
47702             /**
47703              * @event change
47704              * Fires after the active page has been changed.
47705              * @param {Ext.PagingToolbar} this
47706              * @param {Object} pageData An object that has these properties:<ul>
47707              * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
47708              * returned by the server</div></li>
47709              * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
47710              * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
47711              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
47712              * </ul>
47713              */
47714             'change',
47715             /**
47716              * @event beforechange
47717              * Fires just before the active page is changed.
47718              * Return false to prevent the active page from being changed.
47719              * @param {Ext.PagingToolbar} this
47720              * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
47721              * loading the required page. This will contain:<ul>
47722              * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
47723              * be retrieved from the server</div></li>
47724              * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
47725              * </ul>
47726              * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
47727              * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
47728              * <p>Parameters may be added as required in the event handler.</p>
47729              */
47730             'beforechange'
47731         );
47732         this.on('afterlayout', this.onFirstLayout, this, {single: true});
47733         this.cursor = 0;
47734         this.bindStore(this.store, true);
47735     },
47736
47737     // private
47738     onFirstLayout : function(){
47739         if(this.dsLoaded){
47740             this.onLoad.apply(this, this.dsLoaded);
47741         }
47742     },
47743
47744     // private
47745     updateInfo : function(){
47746         if(this.displayItem){
47747             var count = this.store.getCount();
47748             var msg = count == 0 ?
47749                 this.emptyMsg :
47750                 String.format(
47751                     this.displayMsg,
47752                     this.cursor+1, this.cursor+count, this.store.getTotalCount()
47753                 );
47754             this.displayItem.setText(msg);
47755         }
47756     },
47757
47758     // private
47759     onLoad : function(store, r, o){
47760         if(!this.rendered){
47761             this.dsLoaded = [store, r, o];
47762             return;
47763         }
47764         var p = this.getParams();
47765         this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
47766         var d = this.getPageData(), ap = d.activePage, ps = d.pages;
47767
47768         this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
47769         this.inputItem.setValue(ap);
47770         this.first.setDisabled(ap == 1);
47771         this.prev.setDisabled(ap == 1);
47772         this.next.setDisabled(ap == ps);
47773         this.last.setDisabled(ap == ps);
47774         this.refresh.enable();
47775         this.updateInfo();
47776         this.fireEvent('change', this, d);
47777     },
47778
47779     // private
47780     getPageData : function(){
47781         var total = this.store.getTotalCount();
47782         return {
47783             total : total,
47784             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
47785             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
47786         };
47787     },
47788
47789     /**
47790      * Change the active page
47791      * @param {Integer} page The page to display
47792      */
47793     changePage : function(page){
47794         this.doLoad(((page-1) * this.pageSize).constrain(0, this.store.getTotalCount()));
47795     },
47796
47797     // private
47798     onLoadError : function(){
47799         if(!this.rendered){
47800             return;
47801         }
47802         this.refresh.enable();
47803     },
47804
47805     // private
47806     readPage : function(d){
47807         var v = this.inputItem.getValue(), pageNum;
47808         if (!v || isNaN(pageNum = parseInt(v, 10))) {
47809             this.inputItem.setValue(d.activePage);
47810             return false;
47811         }
47812         return pageNum;
47813     },
47814
47815     onPagingFocus : function(){
47816         this.inputItem.select();
47817     },
47818
47819     //private
47820     onPagingBlur : function(e){
47821         this.inputItem.setValue(this.getPageData().activePage);
47822     },
47823
47824     // private
47825     onPagingKeyDown : function(field, e){
47826         var k = e.getKey(), d = this.getPageData(), pageNum;
47827         if (k == e.RETURN) {
47828             e.stopEvent();
47829             pageNum = this.readPage(d);
47830             if(pageNum !== false){
47831                 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
47832                 this.doLoad(pageNum * this.pageSize);
47833             }
47834         }else if (k == e.HOME || k == e.END){
47835             e.stopEvent();
47836             pageNum = k == e.HOME ? 1 : d.pages;
47837             field.setValue(pageNum);
47838         }else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN){
47839             e.stopEvent();
47840             if((pageNum = this.readPage(d))){
47841                 var increment = e.shiftKey ? 10 : 1;
47842                 if(k == e.DOWN || k == e.PAGEDOWN){
47843                     increment *= -1;
47844                 }
47845                 pageNum += increment;
47846                 if(pageNum >= 1 & pageNum <= d.pages){
47847                     field.setValue(pageNum);
47848                 }
47849             }
47850         }
47851     },
47852
47853     // private
47854     getParams : function(){
47855         //retain backwards compat, allow params on the toolbar itself, if they exist.
47856         return this.paramNames || this.store.paramNames;
47857     },
47858
47859     // private
47860     beforeLoad : function(){
47861         if(this.rendered && this.refresh){
47862             this.refresh.disable();
47863         }
47864     },
47865
47866     // private
47867     doLoad : function(start){
47868         var o = {}, pn = this.getParams();
47869         o[pn.start] = start;
47870         o[pn.limit] = this.pageSize;
47871         if(this.fireEvent('beforechange', this, o) !== false){
47872             this.store.load({params:o});
47873         }
47874     },
47875
47876     /**
47877      * Move to the first page, has the same effect as clicking the 'first' button.
47878      */
47879     moveFirst : function(){
47880         this.doLoad(0);
47881     },
47882
47883     /**
47884      * Move to the previous page, has the same effect as clicking the 'previous' button.
47885      */
47886     movePrevious : function(){
47887         this.doLoad(Math.max(0, this.cursor-this.pageSize));
47888     },
47889
47890     /**
47891      * Move to the next page, has the same effect as clicking the 'next' button.
47892      */
47893     moveNext : function(){
47894         this.doLoad(this.cursor+this.pageSize);
47895     },
47896
47897     /**
47898      * Move to the last page, has the same effect as clicking the 'last' button.
47899      */
47900     moveLast : function(){
47901         var total = this.store.getTotalCount(),
47902             extra = total % this.pageSize;
47903
47904         this.doLoad(extra ? (total - extra) : total - this.pageSize);
47905     },
47906
47907     /**
47908      * Refresh the current page, has the same effect as clicking the 'refresh' button.
47909      */
47910     doRefresh : function(){
47911         this.doLoad(this.cursor);
47912     },
47913
47914     /**
47915      * Binds the paging toolbar to the specified {@link Ext.data.Store}
47916      * @param {Store} store The store to bind to this toolbar
47917      * @param {Boolean} initial (Optional) true to not remove listeners
47918      */
47919     bindStore : function(store, initial){
47920         var doLoad;
47921         if(!initial && this.store){
47922             if(store !== this.store && this.store.autoDestroy){
47923                 this.store.destroy();
47924             }else{
47925                 this.store.un('beforeload', this.beforeLoad, this);
47926                 this.store.un('load', this.onLoad, this);
47927                 this.store.un('exception', this.onLoadError, this);
47928             }
47929             if(!store){
47930                 this.store = null;
47931             }
47932         }
47933         if(store){
47934             store = Ext.StoreMgr.lookup(store);
47935             store.on({
47936                 scope: this,
47937                 beforeload: this.beforeLoad,
47938                 load: this.onLoad,
47939                 exception: this.onLoadError
47940             });
47941             doLoad = true;
47942         }
47943         this.store = store;
47944         if(doLoad){
47945             this.onLoad(store, null, {});
47946         }
47947     },
47948
47949     /**
47950      * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
47951      * @param {Ext.data.Store} store The data store to unbind
47952      */
47953     unbind : function(store){
47954         this.bindStore(null);
47955     },
47956
47957     /**
47958      * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
47959      * @param {Ext.data.Store} store The data store to bind
47960      */
47961     bind : function(store){
47962         this.bindStore(store);
47963     },
47964
47965     // private
47966     onDestroy : function(){
47967         this.bindStore(null);
47968         Ext.PagingToolbar.superclass.onDestroy.call(this);
47969     }
47970 });
47971
47972 })();
47973 Ext.reg('paging', Ext.PagingToolbar);/**
47974  * @class Ext.History
47975  * @extends Ext.util.Observable
47976  * History management component that allows you to register arbitrary tokens that signify application
47977  * history state on navigation actions.  You can then handle the history {@link #change} event in order
47978  * to reset your application UI to the appropriate state when the user navigates forward or backward through
47979  * the browser history stack.
47980  * @singleton
47981  */
47982 Ext.History = (function () {
47983     var iframe, hiddenField;
47984     var ready = false;
47985     var currentToken;
47986
47987     function getHash() {
47988         var href = top.location.href, i = href.indexOf("#");
47989         return i >= 0 ? href.substr(i + 1) : null;
47990     }
47991
47992     function doSave() {
47993         hiddenField.value = currentToken;
47994     }
47995
47996     function handleStateChange(token) {
47997         currentToken = token;
47998         Ext.History.fireEvent('change', token);
47999     }
48000
48001     function updateIFrame (token) {
48002         var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');
48003         try {
48004             var doc = iframe.contentWindow.document;
48005             doc.open();
48006             doc.write(html);
48007             doc.close();
48008             return true;
48009         } catch (e) {
48010             return false;
48011         }
48012     }
48013
48014     function checkIFrame() {
48015         if (!iframe.contentWindow || !iframe.contentWindow.document) {
48016             setTimeout(checkIFrame, 10);
48017             return;
48018         }
48019
48020         var doc = iframe.contentWindow.document;
48021         var elem = doc.getElementById("state");
48022         var token = elem ? elem.innerText : null;
48023
48024         var hash = getHash();
48025
48026         setInterval(function () {
48027
48028             doc = iframe.contentWindow.document;
48029             elem = doc.getElementById("state");
48030
48031             var newtoken = elem ? elem.innerText : null;
48032
48033             var newHash = getHash();
48034
48035             if (newtoken !== token) {
48036                 token = newtoken;
48037                 handleStateChange(token);
48038                 top.location.hash = token;
48039                 hash = token;
48040                 doSave();
48041             } else if (newHash !== hash) {
48042                 hash = newHash;
48043                 updateIFrame(newHash);
48044             }
48045
48046         }, 50);
48047
48048         ready = true;
48049
48050         Ext.History.fireEvent('ready', Ext.History);
48051     }
48052
48053     function startUp() {
48054         currentToken = hiddenField.value ? hiddenField.value : getHash();
48055
48056         if (Ext.isIE) {
48057             checkIFrame();
48058         } else {
48059             var hash = getHash();
48060             setInterval(function () {
48061                 var newHash = getHash();
48062                 if (newHash !== hash) {
48063                     hash = newHash;
48064                     handleStateChange(hash);
48065                     doSave();
48066                 }
48067             }, 50);
48068             ready = true;
48069             Ext.History.fireEvent('ready', Ext.History);
48070         }
48071     }
48072
48073     return {
48074         /**
48075          * The id of the hidden field required for storing the current history token.
48076          * @type String
48077          * @property
48078          */
48079         fieldId: 'x-history-field',
48080         /**
48081          * The id of the iframe required by IE to manage the history stack.
48082          * @type String
48083          * @property
48084          */
48085         iframeId: 'x-history-frame',
48086
48087         events:{},
48088
48089         /**
48090          * Initialize the global History instance.
48091          * @param {Boolean} onReady (optional) A callback function that will be called once the history
48092          * component is fully initialized.
48093          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.
48094          */
48095         init: function (onReady, scope) {
48096             if(ready) {
48097                 Ext.callback(onReady, scope, [this]);
48098                 return;
48099             }
48100             if(!Ext.isReady){
48101                 Ext.onReady(function(){
48102                     Ext.History.init(onReady, scope);
48103                 });
48104                 return;
48105             }
48106             hiddenField = Ext.getDom(Ext.History.fieldId);
48107             if (Ext.isIE) {
48108                 iframe = Ext.getDom(Ext.History.iframeId);
48109             }
48110             this.addEvents(
48111                 /**
48112                  * @event ready
48113                  * Fires when the Ext.History singleton has been initialized and is ready for use.
48114                  * @param {Ext.History} The Ext.History singleton.
48115                  */
48116                 'ready',
48117                 /**
48118                  * @event change
48119                  * Fires when navigation back or forwards within the local page's history occurs.
48120                  * @param {String} token An identifier associated with the page state at that point in its history.
48121                  */
48122                 'change'
48123             );
48124             if(onReady){
48125                 this.on('ready', onReady, scope, {single:true});
48126             }
48127             startUp();
48128         },
48129
48130         /**
48131          * Add a new token to the history stack. This can be any arbitrary value, although it would
48132          * commonly be the concatenation of a component id and another id marking the specifc history
48133          * state of that component.  Example usage:
48134          * <pre><code>
48135 // Handle tab changes on a TabPanel
48136 tabPanel.on('tabchange', function(tabPanel, tab){
48137     Ext.History.add(tabPanel.id + ':' + tab.id);
48138 });
48139 </code></pre>
48140          * @param {String} token The value that defines a particular application-specific history state
48141          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token
48142          * it will not save a new history step. Set to false if the same state can be saved more than once
48143          * at the same history stack location (defaults to true).
48144          */
48145         add: function (token, preventDup) {
48146             if(preventDup !== false){
48147                 if(this.getToken() == token){
48148                     return true;
48149                 }
48150             }
48151             if (Ext.isIE) {
48152                 return updateIFrame(token);
48153             } else {
48154                 top.location.hash = token;
48155                 return true;
48156             }
48157         },
48158
48159         /**
48160          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
48161          */
48162         back: function(){
48163             history.go(-1);
48164         },
48165
48166         /**
48167          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
48168          */
48169         forward: function(){
48170             history.go(1);
48171         },
48172
48173         /**
48174          * Retrieves the currently-active history token.
48175          * @return {String} The token
48176          */
48177         getToken: function() {
48178             return ready ? currentToken : getHash();
48179         }
48180     };
48181 })();
48182 Ext.apply(Ext.History, new Ext.util.Observable());/**
48183  * @class Ext.Tip
48184  * @extends Ext.Panel
48185  * @xtype tip
48186  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and
48187  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned
48188  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.
48189  * @constructor
48190  * Create a new Tip
48191  * @param {Object} config The configuration options
48192  */
48193 Ext.Tip = Ext.extend(Ext.Panel, {
48194     /**
48195      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).
48196      */
48197     /**
48198      * @cfg {Number} width
48199      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
48200      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.
48201      */
48202     /**
48203      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).
48204      */
48205     minWidth : 40,
48206     /**
48207      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.
48208      */
48209     maxWidth : 300,
48210     /**
48211      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
48212      * for bottom-right shadow (defaults to "sides").
48213      */
48214     shadow : "sides",
48215     /**
48216      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value
48217      * for this tip relative to its element of origin (defaults to "tl-bl?").
48218      */
48219     defaultAlign : "tl-bl?",
48220     autoRender: true,
48221     quickShowInterval : 250,
48222
48223     // private panel overrides
48224     frame:true,
48225     hidden:true,
48226     baseCls: 'x-tip',
48227     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},
48228     autoHeight:true,
48229
48230     closeAction: 'hide',
48231
48232     // private
48233     initComponent : function(){
48234         Ext.Tip.superclass.initComponent.call(this);
48235         if(this.closable && !this.title){
48236             this.elements += ',header';
48237         }
48238     },
48239
48240     // private
48241     afterRender : function(){
48242         Ext.Tip.superclass.afterRender.call(this);
48243         if(this.closable){
48244             this.addTool({
48245                 id: 'close',
48246                 handler: this[this.closeAction],
48247                 scope: this
48248             });
48249         }
48250     },
48251
48252     /**
48253      * Shows this tip at the specified XY position.  Example usage:
48254      * <pre><code>
48255 // Show the tip at x:50 and y:100
48256 tip.showAt([50,100]);
48257 </code></pre>
48258      * @param {Array} xy An array containing the x and y coordinates
48259      */
48260     showAt : function(xy){
48261         Ext.Tip.superclass.show.call(this);
48262         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){
48263             this.doAutoWidth();
48264         }
48265         if(this.constrainPosition){
48266             xy = this.el.adjustForConstraints(xy);
48267         }
48268         this.setPagePosition(xy[0], xy[1]);
48269     },
48270
48271     // protected
48272     doAutoWidth : function(adjust){
48273         adjust = adjust || 0;
48274         var bw = this.body.getTextWidth();
48275         if(this.title){
48276             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
48277         }
48278         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;
48279         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
48280         
48281         // IE7 repaint bug on initial show
48282         if(Ext.isIE7 && !this.repainted){
48283             this.el.repaint();
48284             this.repainted = true;
48285         }
48286     },
48287
48288     /**
48289      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}
48290      * anchor position value.  Example usage:
48291      * <pre><code>
48292 // Show the tip at the default position ('tl-br?')
48293 tip.showBy('my-el');
48294
48295 // Show the tip's top-left corner anchored to the element's top-right corner
48296 tip.showBy('my-el', 'tl-tr');
48297 </code></pre>
48298      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to
48299      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or
48300      * {@link #defaultAlign} if specified).
48301      */
48302     showBy : function(el, pos){
48303         if(!this.rendered){
48304             this.render(Ext.getBody());
48305         }
48306         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));
48307     },
48308
48309     initDraggable : function(){
48310         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);
48311         this.header.addClass('x-tip-draggable');
48312     }
48313 });
48314
48315 Ext.reg('tip', Ext.Tip);
48316
48317 // private - custom Tip DD implementation
48318 Ext.Tip.DD = function(tip, config){
48319     Ext.apply(this, config);
48320     this.tip = tip;
48321     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);
48322     this.setHandleElId(tip.header.id);
48323     this.scroll = false;
48324 };
48325
48326 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {
48327     moveOnly:true,
48328     scroll:false,
48329     headerOffsets:[100, 25],
48330     startDrag : function(){
48331         this.tip.el.disableShadow();
48332     },
48333     endDrag : function(e){
48334         this.tip.el.enableShadow(true);
48335     }
48336 });/**
48337  * @class Ext.ToolTip
48338  * @extends Ext.Tip
48339  * A standard tooltip implementation for providing additional information when hovering over a target element.
48340  * @xtype tooltip
48341  * @constructor
48342  * Create a new Tooltip
48343  * @param {Object} config The configuration options
48344  */
48345 Ext.ToolTip = Ext.extend(Ext.Tip, {
48346     /**
48347      * When a Tooltip is configured with the <code>{@link #delegate}</code>
48348      * option to cause selected child elements of the <code>{@link #target}</code>
48349      * Element to each trigger a seperate show event, this property is set to
48350      * the DOM element which triggered the show.
48351      * @type DOMElement
48352      * @property triggerElement
48353      */
48354     /**
48355      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor
48356      * for mouseover events to trigger showing this ToolTip.
48357      */
48358     /**
48359      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the
48360      * mouse exits the target element or after the <code>{@link #dismissDelay}</code>
48361      * has expired if set (defaults to true).  If <code>{@link closable} = true</code>
48362      * a close tool button will be rendered into the tooltip header.
48363      */
48364     /**
48365      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays
48366      * after the mouse enters the target element (defaults to 500)
48367      */
48368     showDelay : 500,
48369     /**
48370      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the
48371      * target element but before the tooltip actually hides (defaults to 200).
48372      * Set to 0 for the tooltip to hide immediately.
48373      */
48374     hideDelay : 200,
48375     /**
48376      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip
48377      * automatically hides (defaults to 5000). To disable automatic hiding, set
48378      * dismissDelay = 0.
48379      */
48380     dismissDelay : 5000,
48381     /**
48382      * @cfg {Array} mouseOffset An XY offset from the mouse position where the
48383      * tooltip should be shown (defaults to [15,18]).
48384      */
48385     /**
48386      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it
48387      * moves over the target element (defaults to false).
48388      */
48389     trackMouse : false,
48390     /**
48391      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target
48392      * element, false to anchor it relative to the mouse coordinates (defaults
48393      * to true).  When <code>anchorToTarget</code> is true, use
48394      * <code>{@link #defaultAlign}</code> to control tooltip alignment to the
48395      * target element.  When <code>anchorToTarget</code> is false, use
48396      * <code>{@link #anchorPosition}</code> instead to control alignment.
48397      */
48398     anchorToTarget : true,
48399     /**
48400      * @cfg {Number} anchorOffset A numeric pixel value used to offset the
48401      * default position of the anchor arrow (defaults to 0).  When the anchor
48402      * position is on the top or bottom of the tooltip, <code>anchorOffset</code>
48403      * will be used as a horizontal offset.  Likewise, when the anchor position
48404      * is on the left or right side, <code>anchorOffset</code> will be used as
48405      * a vertical offset.
48406      */
48407     anchorOffset : 0,
48408     /**
48409      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery}
48410      * selector which allows selection of individual elements within the
48411      * <code>{@link #target}</code> element to trigger showing and hiding the
48412      * ToolTip as the mouse moves within the target.</p>
48413      * <p>When specified, the child element of the target which caused a show
48414      * event is placed into the <code>{@link #triggerElement}</code> property
48415      * before the ToolTip is shown.</p>
48416      * <p>This may be useful when a Component has regular, repeating elements
48417      * in it, each of which need a Tooltip which contains information specific
48418      * to that element. For example:</p><pre><code>
48419 var myGrid = new Ext.grid.gridPanel(gridConfig);
48420 myGrid.on('render', function(grid) {
48421     var store = grid.getStore();  // Capture the Store.
48422     var view = grid.getView();    // Capture the GridView.
48423     myGrid.tip = new Ext.ToolTip({
48424         target: view.mainBody,    // The overall target element.
48425         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.
48426         trackMouse: true,         // Moving within the row should not hide the tip.
48427         renderTo: document.body,  // Render immediately so that tip.body can be
48428                                   //  referenced prior to the first show.
48429         listeners: {              // Change content dynamically depending on which element
48430                                   //  triggered the show.
48431             beforeshow: function updateTipBody(tip) {
48432                 var rowIndex = view.findRowIndex(tip.triggerElement);
48433                 tip.body.dom.innerHTML = 'Over Record ID ' + store.getAt(rowIndex).id;
48434             }
48435         }
48436     });
48437 });
48438      *</code></pre>
48439      */
48440
48441     // private
48442     targetCounter : 0,
48443
48444     constrainPosition : false,
48445
48446     // private
48447     initComponent : function(){
48448         Ext.ToolTip.superclass.initComponent.call(this);
48449         this.lastActive = new Date();
48450         this.initTarget(this.target);
48451         this.origAnchor = this.anchor;
48452     },
48453
48454     // private
48455     onRender : function(ct, position){
48456         Ext.ToolTip.superclass.onRender.call(this, ct, position);
48457         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();
48458         this.anchorEl = this.el.createChild({
48459             cls: 'x-tip-anchor ' + this.anchorCls
48460         });
48461     },
48462
48463     // private
48464     afterRender : function(){
48465         Ext.ToolTip.superclass.afterRender.call(this);
48466         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1);
48467     },
48468
48469     /**
48470      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.
48471      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to
48472      */
48473     initTarget : function(target){
48474         var t;
48475         if((t = Ext.get(target))){
48476             if(this.target){
48477                 var tg = Ext.get(this.target);
48478                 this.mun(tg, 'mouseover', this.onTargetOver, this);
48479                 this.mun(tg, 'mouseout', this.onTargetOut, this);
48480                 this.mun(tg, 'mousemove', this.onMouseMove, this);
48481             }
48482             this.mon(t, {
48483                 mouseover: this.onTargetOver,
48484                 mouseout: this.onTargetOut,
48485                 mousemove: this.onMouseMove,
48486                 scope: this
48487             });
48488             this.target = t;
48489         }
48490         if(this.anchor){
48491             this.anchorTarget = this.target;
48492         }
48493     },
48494
48495     // private
48496     onMouseMove : function(e){
48497         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
48498         if (t) {
48499             this.targetXY = e.getXY();
48500             if (t === this.triggerElement) {
48501                 if(!this.hidden && this.trackMouse){
48502                     this.setPagePosition(this.getTargetXY());
48503                 }
48504             } else {
48505                 this.hide();
48506                 this.lastActive = new Date(0);
48507                 this.onTargetOver(e);
48508             }
48509         } else if (!this.closable && this.isVisible()) {
48510             this.hide();
48511         }
48512     },
48513
48514     // private
48515     getTargetXY : function(){
48516         if(this.delegate){
48517             this.anchorTarget = this.triggerElement;
48518         }
48519         if(this.anchor){
48520             this.targetCounter++;
48521             var offsets = this.getOffsets(),
48522                 xy = (this.anchorToTarget && !this.trackMouse) ? this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) : this.targetXY,
48523                 dw = Ext.lib.Dom.getViewWidth() - 5,
48524                 dh = Ext.lib.Dom.getViewHeight() - 5,
48525                 de = document.documentElement,
48526                 bd = document.body,
48527                 scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5,
48528                 scrollY = (de.scrollTop || bd.scrollTop || 0) + 5,
48529                 axy = [xy[0] + offsets[0], xy[1] + offsets[1]],
48530                 sz = this.getSize();
48531                 
48532             this.anchorEl.removeClass(this.anchorCls);
48533
48534             if(this.targetCounter < 2){
48535                 if(axy[0] < scrollX){
48536                     if(this.anchorToTarget){
48537                         this.defaultAlign = 'l-r';
48538                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}
48539                     }
48540                     this.anchor = 'left';
48541                     return this.getTargetXY();
48542                 }
48543                 if(axy[0]+sz.width > dw){
48544                     if(this.anchorToTarget){
48545                         this.defaultAlign = 'r-l';
48546                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}
48547                     }
48548                     this.anchor = 'right';
48549                     return this.getTargetXY();
48550                 }
48551                 if(axy[1] < scrollY){
48552                     if(this.anchorToTarget){
48553                         this.defaultAlign = 't-b';
48554                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}
48555                     }
48556                     this.anchor = 'top';
48557                     return this.getTargetXY();
48558                 }
48559                 if(axy[1]+sz.height > dh){
48560                     if(this.anchorToTarget){
48561                         this.defaultAlign = 'b-t';
48562                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}
48563                     }
48564                     this.anchor = 'bottom';
48565                     return this.getTargetXY();
48566                 }
48567             }
48568
48569             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();
48570             this.anchorEl.addClass(this.anchorCls);
48571             this.targetCounter = 0;
48572             return axy;
48573         }else{
48574             var mouseOffset = this.getMouseOffset();
48575             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];
48576         }
48577     },
48578
48579     getMouseOffset : function(){
48580         var offset = this.anchor ? [0,0] : [15,18];
48581         if(this.mouseOffset){
48582             offset[0] += this.mouseOffset[0];
48583             offset[1] += this.mouseOffset[1];
48584         }
48585         return offset;
48586     },
48587
48588     // private
48589     getAnchorPosition : function(){
48590         if(this.anchor){
48591             this.tipAnchor = this.anchor.charAt(0);
48592         }else{
48593             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
48594             if(!m){
48595                throw 'AnchorTip.defaultAlign is invalid';
48596             }
48597             this.tipAnchor = m[1].charAt(0);
48598         }
48599
48600         switch(this.tipAnchor){
48601             case 't': return 'top';
48602             case 'b': return 'bottom';
48603             case 'r': return 'right';
48604         }
48605         return 'left';
48606     },
48607
48608     // private
48609     getAnchorAlign : function(){
48610         switch(this.anchor){
48611             case 'top'  : return 'tl-bl';
48612             case 'left' : return 'tl-tr';
48613             case 'right': return 'tr-tl';
48614             default     : return 'bl-tl';
48615         }
48616     },
48617
48618     // private
48619     getOffsets : function(){
48620         var offsets, 
48621             ap = this.getAnchorPosition().charAt(0);
48622         if(this.anchorToTarget && !this.trackMouse){
48623             switch(ap){
48624                 case 't':
48625                     offsets = [0, 9];
48626                     break;
48627                 case 'b':
48628                     offsets = [0, -13];
48629                     break;
48630                 case 'r':
48631                     offsets = [-13, 0];
48632                     break;
48633                 default:
48634                     offsets = [9, 0];
48635                     break;
48636             }
48637         }else{
48638             switch(ap){
48639                 case 't':
48640                     offsets = [-15-this.anchorOffset, 30];
48641                     break;
48642                 case 'b':
48643                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];
48644                     break;
48645                 case 'r':
48646                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];
48647                     break;
48648                 default:
48649                     offsets = [25, -13-this.anchorOffset];
48650                     break;
48651             }
48652         }
48653         var mouseOffset = this.getMouseOffset();
48654         offsets[0] += mouseOffset[0];
48655         offsets[1] += mouseOffset[1];
48656
48657         return offsets;
48658     },
48659
48660     // private
48661     onTargetOver : function(e){
48662         if(this.disabled || e.within(this.target.dom, true)){
48663             return;
48664         }
48665         var t = e.getTarget(this.delegate);
48666         if (t) {
48667             this.triggerElement = t;
48668             this.clearTimer('hide');
48669             this.targetXY = e.getXY();
48670             this.delayShow();
48671         }
48672     },
48673
48674     // private
48675     delayShow : function(){
48676         if(this.hidden && !this.showTimer){
48677             if(this.lastActive.getElapsed() < this.quickShowInterval){
48678                 this.show();
48679             }else{
48680                 this.showTimer = this.show.defer(this.showDelay, this);
48681             }
48682         }else if(!this.hidden && this.autoHide !== false){
48683             this.show();
48684         }
48685     },
48686
48687     // private
48688     onTargetOut : function(e){
48689         if(this.disabled || e.within(this.target.dom, true)){
48690             return;
48691         }
48692         this.clearTimer('show');
48693         if(this.autoHide !== false){
48694             this.delayHide();
48695         }
48696     },
48697
48698     // private
48699     delayHide : function(){
48700         if(!this.hidden && !this.hideTimer){
48701             this.hideTimer = this.hide.defer(this.hideDelay, this);
48702         }
48703     },
48704
48705     /**
48706      * Hides this tooltip if visible.
48707      */
48708     hide: function(){
48709         this.clearTimer('dismiss');
48710         this.lastActive = new Date();
48711         if(this.anchorEl){
48712             this.anchorEl.hide();
48713         }
48714         Ext.ToolTip.superclass.hide.call(this);
48715         delete this.triggerElement;
48716     },
48717
48718     /**
48719      * Shows this tooltip at the current event target XY position.
48720      */
48721     show : function(){
48722         if(this.anchor){
48723             // pre-show it off screen so that the el will have dimensions
48724             // for positioning calcs when getting xy next
48725             this.showAt([-1000,-1000]);
48726             this.origConstrainPosition = this.constrainPosition;
48727             this.constrainPosition = false;
48728             this.anchor = this.origAnchor;
48729         }
48730         this.showAt(this.getTargetXY());
48731
48732         if(this.anchor){
48733             this.syncAnchor();
48734             this.anchorEl.show();
48735             this.constrainPosition = this.origConstrainPosition;
48736         }else{
48737             this.anchorEl.hide();
48738         }
48739     },
48740
48741     // inherit docs
48742     showAt : function(xy){
48743         this.lastActive = new Date();
48744         this.clearTimers();
48745         Ext.ToolTip.superclass.showAt.call(this, xy);
48746         if(this.dismissDelay && this.autoHide !== false){
48747             this.dismissTimer = this.hide.defer(this.dismissDelay, this);
48748         }
48749         if(this.anchor && !this.anchorEl.isVisible()){
48750             this.syncAnchor();
48751             this.anchorEl.show();
48752         }
48753     },
48754
48755     // private
48756     syncAnchor : function(){
48757         var anchorPos, targetPos, offset;
48758         switch(this.tipAnchor.charAt(0)){
48759             case 't':
48760                 anchorPos = 'b';
48761                 targetPos = 'tl';
48762                 offset = [20+this.anchorOffset, 2];
48763                 break;
48764             case 'r':
48765                 anchorPos = 'l';
48766                 targetPos = 'tr';
48767                 offset = [-2, 11+this.anchorOffset];
48768                 break;
48769             case 'b':
48770                 anchorPos = 't';
48771                 targetPos = 'bl';
48772                 offset = [20+this.anchorOffset, -2];
48773                 break;
48774             default:
48775                 anchorPos = 'r';
48776                 targetPos = 'tl';
48777                 offset = [2, 11+this.anchorOffset];
48778                 break;
48779         }
48780         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);
48781     },
48782
48783     // private
48784     setPagePosition : function(x, y){
48785         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);
48786         if(this.anchor){
48787             this.syncAnchor();
48788         }
48789     },
48790
48791     // private
48792     clearTimer : function(name){
48793         name = name + 'Timer';
48794         clearTimeout(this[name]);
48795         delete this[name];
48796     },
48797
48798     // private
48799     clearTimers : function(){
48800         this.clearTimer('show');
48801         this.clearTimer('dismiss');
48802         this.clearTimer('hide');
48803     },
48804
48805     // private
48806     onShow : function(){
48807         Ext.ToolTip.superclass.onShow.call(this);
48808         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);
48809     },
48810
48811     // private
48812     onHide : function(){
48813         Ext.ToolTip.superclass.onHide.call(this);
48814         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
48815     },
48816
48817     // private
48818     onDocMouseDown : function(e){
48819         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){
48820             this.disable();
48821             this.doEnable.defer(100, this);
48822         }
48823     },
48824     
48825     // private
48826     doEnable : function(){
48827         if(!this.isDestroyed){
48828             this.enable();
48829         }
48830     },
48831
48832     // private
48833     onDisable : function(){
48834         this.clearTimers();
48835         this.hide();
48836     },
48837
48838     // private
48839     adjustPosition : function(x, y){
48840         if(this.contstrainPosition){
48841             var ay = this.targetXY[1], h = this.getSize().height;
48842             if(y <= ay && (y+h) >= ay){
48843                 y = ay-h-5;
48844             }
48845         }
48846         return {x : x, y: y};
48847     },
48848     
48849     beforeDestroy : function(){
48850         this.clearTimers();
48851         Ext.destroy(this.anchorEl);
48852         delete this.anchorEl;
48853         delete this.target;
48854         delete this.anchorTarget;
48855         delete this.triggerElement;
48856         Ext.ToolTip.superclass.beforeDestroy.call(this);    
48857     },
48858
48859     // private
48860     onDestroy : function(){
48861         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
48862         Ext.ToolTip.superclass.onDestroy.call(this);
48863     }
48864 });
48865
48866 Ext.reg('tooltip', Ext.ToolTip);/**
48867  * @class Ext.QuickTip
48868  * @extends Ext.ToolTip
48869  * @xtype quicktip
48870  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global
48871  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.
48872  * @constructor
48873  * Create a new Tip
48874  * @param {Object} config The configuration options
48875  */
48876 Ext.QuickTip = Ext.extend(Ext.ToolTip, {
48877     /**
48878      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).
48879      */
48880     /**
48881      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).
48882      */
48883     interceptTitles : false,
48884
48885     // private
48886     tagConfig : {
48887         namespace : "ext",
48888         attribute : "qtip",
48889         width : "qwidth",
48890         target : "target",
48891         title : "qtitle",
48892         hide : "hide",
48893         cls : "qclass",
48894         align : "qalign",
48895         anchor : "anchor"
48896     },
48897
48898     // private
48899     initComponent : function(){
48900         this.target = this.target || Ext.getDoc();
48901         this.targets = this.targets || {};
48902         Ext.QuickTip.superclass.initComponent.call(this);
48903     },
48904
48905     /**
48906      * Configures a new quick tip instance and assigns it to a target element.  The following config values are
48907      * supported (for example usage, see the {@link Ext.QuickTips} class header):
48908      * <div class="mdetail-params"><ul>
48909      * <li>autoHide</li>
48910      * <li>cls</li>
48911      * <li>dismissDelay (overrides the singleton value)</li>
48912      * <li>target (required)</li>
48913      * <li>text (required)</li>
48914      * <li>title</li>
48915      * <li>width</li></ul></div>
48916      * @param {Object} config The config object
48917      */
48918     register : function(config){
48919         var cs = Ext.isArray(config) ? config : arguments;
48920         for(var i = 0, len = cs.length; i < len; i++){
48921             var c = cs[i];
48922             var target = c.target;
48923             if(target){
48924                 if(Ext.isArray(target)){
48925                     for(var j = 0, jlen = target.length; j < jlen; j++){
48926                         this.targets[Ext.id(target[j])] = c;
48927                     }
48928                 } else{
48929                     this.targets[Ext.id(target)] = c;
48930                 }
48931             }
48932         }
48933     },
48934
48935     /**
48936      * Removes this quick tip from its element and destroys it.
48937      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
48938      */
48939     unregister : function(el){
48940         delete this.targets[Ext.id(el)];
48941     },
48942     
48943     /**
48944      * Hides a visible tip or cancels an impending show for a particular element.
48945      * @param {String/HTMLElement/Element} el The element that is the target of the tip.
48946      */
48947     cancelShow: function(el){
48948         var at = this.activeTarget;
48949         el = Ext.get(el).dom;
48950         if(this.isVisible()){
48951             if(at && at.el == el){
48952                 this.hide();
48953             }
48954         }else if(at && at.el == el){
48955             this.clearTimer('show');
48956         }
48957     },
48958     
48959     getTipCfg: function(e) {
48960         var t = e.getTarget(), 
48961             ttp, 
48962             cfg;
48963         if(this.interceptTitles && t.title && Ext.isString(t.title)){
48964             ttp = t.title;
48965             t.qtip = ttp;
48966             t.removeAttribute("title");
48967             e.preventDefault();
48968         }else{
48969             cfg = this.tagConfig;
48970             ttp = t.qtip || Ext.fly(t).getAttribute(cfg.attribute, cfg.namespace);
48971         }
48972         return ttp;
48973     },
48974
48975     // private
48976     onTargetOver : function(e){
48977         if(this.disabled){
48978             return;
48979         }
48980         this.targetXY = e.getXY();
48981         var t = e.getTarget();
48982         if(!t || t.nodeType !== 1 || t == document || t == document.body){
48983             return;
48984         }
48985         if(this.activeTarget && ((t == this.activeTarget.el) || Ext.fly(this.activeTarget.el).contains(t))){
48986             this.clearTimer('hide');
48987             this.show();
48988             return;
48989         }
48990         if(t && this.targets[t.id]){
48991             this.activeTarget = this.targets[t.id];
48992             this.activeTarget.el = t;
48993             this.anchor = this.activeTarget.anchor;
48994             if(this.anchor){
48995                 this.anchorTarget = t;
48996             }
48997             this.delayShow();
48998             return;
48999         }
49000         var ttp, et = Ext.fly(t), cfg = this.tagConfig, ns = cfg.namespace;
49001         if(ttp = this.getTipCfg(e)){
49002             var autoHide = et.getAttribute(cfg.hide, ns);
49003             this.activeTarget = {
49004                 el: t,
49005                 text: ttp,
49006                 width: et.getAttribute(cfg.width, ns),
49007                 autoHide: autoHide != "user" && autoHide !== 'false',
49008                 title: et.getAttribute(cfg.title, ns),
49009                 cls: et.getAttribute(cfg.cls, ns),
49010                 align: et.getAttribute(cfg.align, ns)
49011                 
49012             };
49013             this.anchor = et.getAttribute(cfg.anchor, ns);
49014             if(this.anchor){
49015                 this.anchorTarget = t;
49016             }
49017             this.delayShow();
49018         }
49019     },
49020
49021     // private
49022     onTargetOut : function(e){
49023
49024         // If moving within the current target, and it does not have a new tip, ignore the mouseout
49025         if (this.activeTarget && e.within(this.activeTarget.el) && !this.getTipCfg(e)) {
49026             return;
49027         }
49028
49029         this.clearTimer('show');
49030         if(this.autoHide !== false){
49031             this.delayHide();
49032         }
49033     },
49034
49035     // inherit docs
49036     showAt : function(xy){
49037         var t = this.activeTarget;
49038         if(t){
49039             if(!this.rendered){
49040                 this.render(Ext.getBody());
49041                 this.activeTarget = t;
49042             }
49043             if(t.width){
49044                 this.setWidth(t.width);
49045                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));
49046                 this.measureWidth = false;
49047             } else{
49048                 this.measureWidth = true;
49049             }
49050             this.setTitle(t.title || '');
49051             this.body.update(t.text);
49052             this.autoHide = t.autoHide;
49053             this.dismissDelay = t.dismissDelay || this.dismissDelay;
49054             if(this.lastCls){
49055                 this.el.removeClass(this.lastCls);
49056                 delete this.lastCls;
49057             }
49058             if(t.cls){
49059                 this.el.addClass(t.cls);
49060                 this.lastCls = t.cls;
49061             }
49062             if(this.anchor){
49063                 this.constrainPosition = false;
49064             }else if(t.align){ // TODO: this doesn't seem to work consistently
49065                 xy = this.el.getAlignToXY(t.el, t.align);
49066                 this.constrainPosition = false;
49067             }else{
49068                 this.constrainPosition = true;
49069             }
49070         }
49071         Ext.QuickTip.superclass.showAt.call(this, xy);
49072     },
49073
49074     // inherit docs
49075     hide: function(){
49076         delete this.activeTarget;
49077         Ext.QuickTip.superclass.hide.call(this);
49078     }
49079 });
49080 Ext.reg('quicktip', Ext.QuickTip);/**
49081  * @class Ext.QuickTips
49082  * <p>Provides attractive and customizable tooltips for any element. The QuickTips
49083  * singleton is used to configure and manage tooltips globally for multiple elements
49084  * in a generic manner.  To create individual tooltips with maximum customizability,
49085  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>
49086  * <p>Quicktips can be configured via tag attributes directly in markup, or by
49087  * registering quick tips programmatically via the {@link #register} method.</p>
49088  * <p>The singleton's instance of {@link Ext.QuickTip} is available via
49089  * {@link #getQuickTip}, and supports all the methods, and all the all the
49090  * configuration properties of Ext.QuickTip. These settings will apply to all
49091  * tooltips shown by the singleton.</p>
49092  * <p>Below is the summary of the configuration properties which can be used.
49093  * For detailed descriptions see {@link #getQuickTip}</p>
49094  * <p><b>QuickTips singleton configs (all are optional)</b></p>
49095  * <div class="mdetail-params"><ul><li>dismissDelay</li>
49096  * <li>hideDelay</li>
49097  * <li>maxWidth</li>
49098  * <li>minWidth</li>
49099  * <li>showDelay</li>
49100  * <li>trackMouse</li></ul></div>
49101  * <p><b>Target element configs (optional unless otherwise noted)</b></p>
49102  * <div class="mdetail-params"><ul><li>autoHide</li>
49103  * <li>cls</li>
49104  * <li>dismissDelay (overrides singleton value)</li>
49105  * <li>target (required)</li>
49106  * <li>text (required)</li>
49107  * <li>title</li>
49108  * <li>width</li></ul></div>
49109  * <p>Here is an example showing how some of these config options could be used:</p>
49110  * <pre><code>
49111 // Init the singleton.  Any tag-based quick tips will start working.
49112 Ext.QuickTips.init();
49113
49114 // Apply a set of config properties to the singleton
49115 Ext.apply(Ext.QuickTips.getQuickTip(), {
49116     maxWidth: 200,
49117     minWidth: 100,
49118     showDelay: 50,
49119     trackMouse: true
49120 });
49121
49122 // Manually register a quick tip for a specific element
49123 Ext.QuickTips.register({
49124     target: 'my-div',
49125     title: 'My Tooltip',
49126     text: 'This tooltip was added in code',
49127     width: 100,
49128     dismissDelay: 20
49129 });
49130 </code></pre>
49131  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with
49132  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary
49133  * of supported attributes (optional unless otherwise noted):</p>
49134  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the
49135  * same as autoHide = true.</li>
49136  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>
49137  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>
49138  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>
49139  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>
49140  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>
49141  * <pre><code>
49142 // Add a quick tip to an HTML button
49143 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"
49144      ext:qtip="This is a quick tip from markup!">&lt;/input>
49145 </code></pre>
49146  * @singleton
49147  */
49148 Ext.QuickTips = function(){
49149     var tip, locks = [];
49150     return {
49151         /**
49152          * Initialize the global QuickTips instance and prepare any quick tips.
49153          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) 
49154          */
49155         init : function(autoRender){
49156             if(!tip){
49157                 if(!Ext.isReady){
49158                     Ext.onReady(function(){
49159                         Ext.QuickTips.init(autoRender);
49160                     });
49161                     return;
49162                 }
49163                 tip = new Ext.QuickTip({elements:'header,body'});
49164                 if(autoRender !== false){
49165                     tip.render(Ext.getBody());
49166                 }
49167             }
49168         },
49169
49170         /**
49171          * Enable quick tips globally.
49172          */
49173         enable : function(){
49174             if(tip){
49175                 locks.pop();
49176                 if(locks.length < 1){
49177                     tip.enable();
49178                 }
49179             }
49180         },
49181
49182         /**
49183          * Disable quick tips globally.
49184          */
49185         disable : function(){
49186             if(tip){
49187                 tip.disable();
49188             }
49189             locks.push(1);
49190         },
49191
49192         /**
49193          * Returns true if quick tips are enabled, else false.
49194          * @return {Boolean}
49195          */
49196         isEnabled : function(){
49197             return tip !== undefined && !tip.disabled;
49198         },
49199
49200         /**
49201          * Gets the global QuickTips instance.
49202          */
49203         getQuickTip : function(){
49204             return tip;
49205         },
49206
49207         /**
49208          * Configures a new quick tip instance and assigns it to a target element.  See
49209          * {@link Ext.QuickTip#register} for details.
49210          * @param {Object} config The config object
49211          */
49212         register : function(){
49213             tip.register.apply(tip, arguments);
49214         },
49215
49216         /**
49217          * Removes any registered quick tip from the target element and destroys it.
49218          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
49219          */
49220         unregister : function(){
49221             tip.unregister.apply(tip, arguments);
49222         },
49223
49224         /**
49225          * Alias of {@link #register}.
49226          * @param {Object} config The config object
49227          */
49228         tips :function(){
49229             tip.register.apply(tip, arguments);
49230         }
49231     }
49232 }();/**
49233  * @class Ext.slider.Tip
49234  * @extends Ext.Tip
49235  * Simple plugin for using an Ext.Tip with a slider to show the slider value. Example usage:
49236 <pre>
49237 new Ext.Slider({
49238     width: 214,
49239     minValue: 0,
49240     maxValue: 100,
49241     plugins: new Ext.slider.Tip()
49242 });
49243 </pre>
49244  * Optionally provide your own tip text by overriding getText:
49245  <pre>
49246  new Ext.Slider({
49247      width: 214,
49248      minValue: 0,
49249      maxValue: 100,
49250      plugins: new Ext.slider.Tip({
49251          getText: function(thumb){
49252              return String.format('<b>{0}% complete</b>', thumb.value);
49253          }
49254      })
49255  });
49256  </pre>
49257  */
49258 Ext.slider.Tip = Ext.extend(Ext.Tip, {
49259     minWidth: 10,
49260     offsets : [0, -10],
49261     
49262     init: function(slider) {
49263         slider.on({
49264             scope    : this,
49265             dragstart: this.onSlide,
49266             drag     : this.onSlide,
49267             dragend  : this.hide,
49268             destroy  : this.destroy
49269         });
49270     },
49271     
49272     /**
49273      * @private
49274      * Called whenever a dragstart or drag event is received on the associated Thumb. 
49275      * Aligns the Tip with the Thumb's new position.
49276      * @param {Ext.slider.MultiSlider} slider The slider
49277      * @param {Ext.EventObject} e The Event object
49278      * @param {Ext.slider.Thumb} thumb The thumb that the Tip is attached to
49279      */
49280     onSlide : function(slider, e, thumb) {
49281         this.show();
49282         this.body.update(this.getText(thumb));
49283         this.doAutoWidth();
49284         this.el.alignTo(thumb.el, 'b-t?', this.offsets);
49285     },
49286
49287     /**
49288      * Used to create the text that appears in the Tip's body. By default this just returns
49289      * the value of the Slider Thumb that the Tip is attached to. Override to customize.
49290      * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to
49291      * @return {String} The text to display in the tip
49292      */
49293     getText : function(thumb) {
49294         return String(thumb.value);
49295     }
49296 });
49297
49298 //backwards compatibility - SliderTip used to be a ux before 3.2
49299 Ext.ux.SliderTip = Ext.slider.Tip;/**
49300  * @class Ext.tree.TreePanel
49301  * @extends Ext.Panel
49302  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>
49303  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata
49304  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>
49305  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be
49306  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.
49307  * <p>An example of tree rendered to an existing div:</p><pre><code>
49308 var tree = new Ext.tree.TreePanel({
49309     renderTo: 'tree-div',
49310     useArrows: true,
49311     autoScroll: true,
49312     animate: true,
49313     enableDD: true,
49314     containerScroll: true,
49315     border: false,
49316     // auto create TreeLoader
49317     dataUrl: 'get-nodes.php',
49318
49319     root: {
49320         nodeType: 'async',
49321         text: 'Ext JS',
49322         draggable: false,
49323         id: 'source'
49324     }
49325 });
49326
49327 tree.getRootNode().expand();
49328  * </code></pre>
49329  * <p>The example above would work with a data packet similar to this:</p><pre><code>
49330 [{
49331     "text": "adapter",
49332     "id": "source\/adapter",
49333     "cls": "folder"
49334 }, {
49335     "text": "dd",
49336     "id": "source\/dd",
49337     "cls": "folder"
49338 }, {
49339     "text": "debug.js",
49340     "id": "source\/debug.js",
49341     "leaf": true,
49342     "cls": "file"
49343 }]
49344  * </code></pre>
49345  * <p>An example of tree within a Viewport:</p><pre><code>
49346 new Ext.Viewport({
49347     layout: 'border',
49348     items: [{
49349         region: 'west',
49350         collapsible: true,
49351         title: 'Navigation',
49352         xtype: 'treepanel',
49353         width: 200,
49354         autoScroll: true,
49355         split: true,
49356         loader: new Ext.tree.TreeLoader(),
49357         root: new Ext.tree.AsyncTreeNode({
49358             expanded: true,
49359             children: [{
49360                 text: 'Menu Option 1',
49361                 leaf: true
49362             }, {
49363                 text: 'Menu Option 2',
49364                 leaf: true
49365             }, {
49366                 text: 'Menu Option 3',
49367                 leaf: true
49368             }]
49369         }),
49370         rootVisible: false,
49371         listeners: {
49372             click: function(n) {
49373                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');
49374             }
49375         }
49376     }, {
49377         region: 'center',
49378         xtype: 'tabpanel',
49379         // remaining code not shown ...
49380     }]
49381 });
49382 </code></pre>
49383  *
49384  * @cfg {Ext.tree.TreeNode} root The root node for the tree.
49385  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)
49386  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)
49387  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop
49388  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag
49389  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop
49390  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance
49391  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance
49392  * @cfg {String} ddGroup The DD group this TreePanel belongs to
49393  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)
49394  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling
49395  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager
49396  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})
49397  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)
49398  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})
49399  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded
49400  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})
49401  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting
49402  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel
49403  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)
49404  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)
49405  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).
49406  *
49407  * @constructor
49408  * @param {Object} config
49409  * @xtype treepanel
49410  */
49411 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {
49412     rootVisible : true,
49413     animate : Ext.enableFx,
49414     lines : true,
49415     enableDD : false,
49416     hlDrop : Ext.enableFx,
49417     pathSeparator : '/',
49418
49419     /**
49420      * @cfg {Array} bubbleEvents
49421      * <p>An array of events that, when fired, should be bubbled to any parent container.
49422      * See {@link Ext.util.Observable#enableBubble}.
49423      * Defaults to <tt>[]</tt>.
49424      */
49425     bubbleEvents : [],
49426
49427     initComponent : function(){
49428         Ext.tree.TreePanel.superclass.initComponent.call(this);
49429
49430         if(!this.eventModel){
49431             this.eventModel = new Ext.tree.TreeEventModel(this);
49432         }
49433
49434         // initialize the loader
49435         var l = this.loader;
49436         if(!l){
49437             l = new Ext.tree.TreeLoader({
49438                 dataUrl: this.dataUrl,
49439                 requestMethod: this.requestMethod
49440             });
49441         }else if(Ext.isObject(l) && !l.load){
49442             l = new Ext.tree.TreeLoader(l);
49443         }
49444         this.loader = l;
49445
49446         this.nodeHash = {};
49447
49448         /**
49449         * The root node of this tree.
49450         * @type Ext.tree.TreeNode
49451         * @property root
49452         */
49453         if(this.root){
49454             var r = this.root;
49455             delete this.root;
49456             this.setRootNode(r);
49457         }
49458
49459
49460         this.addEvents(
49461
49462             /**
49463             * @event append
49464             * Fires when a new child node is appended to a node in this tree.
49465             * @param {Tree} tree The owner tree
49466             * @param {Node} parent The parent node
49467             * @param {Node} node The newly appended node
49468             * @param {Number} index The index of the newly appended node
49469             */
49470            'append',
49471            /**
49472             * @event remove
49473             * Fires when a child node is removed from a node in this tree.
49474             * @param {Tree} tree The owner tree
49475             * @param {Node} parent The parent node
49476             * @param {Node} node The child node removed
49477             */
49478            'remove',
49479            /**
49480             * @event movenode
49481             * Fires when a node is moved to a new location in the tree
49482             * @param {Tree} tree The owner tree
49483             * @param {Node} node The node moved
49484             * @param {Node} oldParent The old parent of this node
49485             * @param {Node} newParent The new parent of this node
49486             * @param {Number} index The index it was moved to
49487             */
49488            'movenode',
49489            /**
49490             * @event insert
49491             * Fires when a new child node is inserted in a node in this tree.
49492             * @param {Tree} tree The owner tree
49493             * @param {Node} parent The parent node
49494             * @param {Node} node The child node inserted
49495             * @param {Node} refNode The child node the node was inserted before
49496             */
49497            'insert',
49498            /**
49499             * @event beforeappend
49500             * Fires before a new child is appended to a node in this tree, return false to cancel the append.
49501             * @param {Tree} tree The owner tree
49502             * @param {Node} parent The parent node
49503             * @param {Node} node The child node to be appended
49504             */
49505            'beforeappend',
49506            /**
49507             * @event beforeremove
49508             * Fires before a child is removed from a node in this tree, return false to cancel the remove.
49509             * @param {Tree} tree The owner tree
49510             * @param {Node} parent The parent node
49511             * @param {Node} node The child node to be removed
49512             */
49513            'beforeremove',
49514            /**
49515             * @event beforemovenode
49516             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
49517             * @param {Tree} tree The owner tree
49518             * @param {Node} node The node being moved
49519             * @param {Node} oldParent The parent of the node
49520             * @param {Node} newParent The new parent the node is moving to
49521             * @param {Number} index The index it is being moved to
49522             */
49523            'beforemovenode',
49524            /**
49525             * @event beforeinsert
49526             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
49527             * @param {Tree} tree The owner tree
49528             * @param {Node} parent The parent node
49529             * @param {Node} node The child node to be inserted
49530             * @param {Node} refNode The child node the node is being inserted before
49531             */
49532             'beforeinsert',
49533
49534             /**
49535             * @event beforeload
49536             * Fires before a node is loaded, return false to cancel
49537             * @param {Node} node The node being loaded
49538             */
49539             'beforeload',
49540             /**
49541             * @event load
49542             * Fires when a node is loaded
49543             * @param {Node} node The node that was loaded
49544             */
49545             'load',
49546             /**
49547             * @event textchange
49548             * Fires when the text for a node is changed
49549             * @param {Node} node The node
49550             * @param {String} text The new text
49551             * @param {String} oldText The old text
49552             */
49553             'textchange',
49554             /**
49555             * @event beforeexpandnode
49556             * Fires before a node is expanded, return false to cancel.
49557             * @param {Node} node The node
49558             * @param {Boolean} deep
49559             * @param {Boolean} anim
49560             */
49561             'beforeexpandnode',
49562             /**
49563             * @event beforecollapsenode
49564             * Fires before a node is collapsed, return false to cancel.
49565             * @param {Node} node The node
49566             * @param {Boolean} deep
49567             * @param {Boolean} anim
49568             */
49569             'beforecollapsenode',
49570             /**
49571             * @event expandnode
49572             * Fires when a node is expanded
49573             * @param {Node} node The node
49574             */
49575             'expandnode',
49576             /**
49577             * @event disabledchange
49578             * Fires when the disabled status of a node changes
49579             * @param {Node} node The node
49580             * @param {Boolean} disabled
49581             */
49582             'disabledchange',
49583             /**
49584             * @event collapsenode
49585             * Fires when a node is collapsed
49586             * @param {Node} node The node
49587             */
49588             'collapsenode',
49589             /**
49590             * @event beforeclick
49591             * Fires before click processing on a node. Return false to cancel the default action.
49592             * @param {Node} node The node
49593             * @param {Ext.EventObject} e The event object
49594             */
49595             'beforeclick',
49596             /**
49597             * @event click
49598             * Fires when a node is clicked
49599             * @param {Node} node The node
49600             * @param {Ext.EventObject} e The event object
49601             */
49602             'click',
49603             /**
49604             * @event containerclick
49605             * Fires when the tree container is clicked
49606             * @param {Tree} this
49607             * @param {Ext.EventObject} e The event object
49608             */
49609             'containerclick',
49610             /**
49611             * @event checkchange
49612             * Fires when a node with a checkbox's checked property changes
49613             * @param {Node} this This node
49614             * @param {Boolean} checked
49615             */
49616             'checkchange',
49617             /**
49618             * @event beforedblclick
49619             * Fires before double click processing on a node. Return false to cancel the default action.
49620             * @param {Node} node The node
49621             * @param {Ext.EventObject} e The event object
49622             */
49623             'beforedblclick',
49624             /**
49625             * @event dblclick
49626             * Fires when a node is double clicked
49627             * @param {Node} node The node
49628             * @param {Ext.EventObject} e The event object
49629             */
49630             'dblclick',
49631             /**
49632             * @event containerdblclick
49633             * Fires when the tree container is double clicked
49634             * @param {Tree} this
49635             * @param {Ext.EventObject} e The event object
49636             */
49637             'containerdblclick',
49638             /**
49639             * @event contextmenu
49640             * Fires when a node is right clicked. To display a context menu in response to this
49641             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add
49642             * a handler for this event:<pre><code>
49643 new Ext.tree.TreePanel({
49644     title: 'My TreePanel',
49645     root: new Ext.tree.AsyncTreeNode({
49646         text: 'The Root',
49647         children: [
49648             { text: 'Child node 1', leaf: true },
49649             { text: 'Child node 2', leaf: true }
49650         ]
49651     }),
49652     contextMenu: new Ext.menu.Menu({
49653         items: [{
49654             id: 'delete-node',
49655             text: 'Delete Node'
49656         }],
49657         listeners: {
49658             itemclick: function(item) {
49659                 switch (item.id) {
49660                     case 'delete-node':
49661                         var n = item.parentMenu.contextNode;
49662                         if (n.parentNode) {
49663                             n.remove();
49664                         }
49665                         break;
49666                 }
49667             }
49668         }
49669     }),
49670     listeners: {
49671         contextmenu: function(node, e) {
49672 //          Register the context node with the menu so that a Menu Item's handler function can access
49673 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.
49674             node.select();
49675             var c = node.getOwnerTree().contextMenu;
49676             c.contextNode = node;
49677             c.showAt(e.getXY());
49678         }
49679     }
49680 });
49681 </code></pre>
49682             * @param {Node} node The node
49683             * @param {Ext.EventObject} e The event object
49684             */
49685             'contextmenu',
49686             /**
49687             * @event containercontextmenu
49688             * Fires when the tree container is right clicked
49689             * @param {Tree} this
49690             * @param {Ext.EventObject} e The event object
49691             */
49692             'containercontextmenu',
49693             /**
49694             * @event beforechildrenrendered
49695             * Fires right before the child nodes for a node are rendered
49696             * @param {Node} node The node
49697             */
49698             'beforechildrenrendered',
49699            /**
49700              * @event startdrag
49701              * Fires when a node starts being dragged
49702              * @param {Ext.tree.TreePanel} this
49703              * @param {Ext.tree.TreeNode} node
49704              * @param {event} e The raw browser event
49705              */
49706             'startdrag',
49707             /**
49708              * @event enddrag
49709              * Fires when a drag operation is complete
49710              * @param {Ext.tree.TreePanel} this
49711              * @param {Ext.tree.TreeNode} node
49712              * @param {event} e The raw browser event
49713              */
49714             'enddrag',
49715             /**
49716              * @event dragdrop
49717              * Fires when a dragged node is dropped on a valid DD target
49718              * @param {Ext.tree.TreePanel} this
49719              * @param {Ext.tree.TreeNode} node
49720              * @param {DD} dd The dd it was dropped on
49721              * @param {event} e The raw browser event
49722              */
49723             'dragdrop',
49724             /**
49725              * @event beforenodedrop
49726              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
49727              * passed to handlers has the following properties:<br />
49728              * <ul style="padding:5px;padding-left:16px;">
49729              * <li>tree - The TreePanel</li>
49730              * <li>target - The node being targeted for the drop</li>
49731              * <li>data - The drag data from the drag source</li>
49732              * <li>point - The point of the drop - append, above or below</li>
49733              * <li>source - The drag source</li>
49734              * <li>rawEvent - Raw mouse event</li>
49735              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
49736              * to be inserted by setting them on this object.</li>
49737              * <li>cancel - Set this to true to cancel the drop.</li>
49738              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true
49739              * will prevent the animated 'repair' from appearing.</li>
49740              * </ul>
49741              * @param {Object} dropEvent
49742              */
49743             'beforenodedrop',
49744             /**
49745              * @event nodedrop
49746              * Fires after a DD object is dropped on a node in this tree. The dropEvent
49747              * passed to handlers has the following properties:<br />
49748              * <ul style="padding:5px;padding-left:16px;">
49749              * <li>tree - The TreePanel</li>
49750              * <li>target - The node being targeted for the drop</li>
49751              * <li>data - The drag data from the drag source</li>
49752              * <li>point - The point of the drop - append, above or below</li>
49753              * <li>source - The drag source</li>
49754              * <li>rawEvent - Raw mouse event</li>
49755              * <li>dropNode - Dropped node(s).</li>
49756              * </ul>
49757              * @param {Object} dropEvent
49758              */
49759             'nodedrop',
49760              /**
49761              * @event nodedragover
49762              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
49763              * passed to handlers has the following properties:<br />
49764              * <ul style="padding:5px;padding-left:16px;">
49765              * <li>tree - The TreePanel</li>
49766              * <li>target - The node being targeted for the drop</li>
49767              * <li>data - The drag data from the drag source</li>
49768              * <li>point - The point of the drop - append, above or below</li>
49769              * <li>source - The drag source</li>
49770              * <li>rawEvent - Raw mouse event</li>
49771              * <li>dropNode - Drop node(s) provided by the source.</li>
49772              * <li>cancel - Set this to true to signal drop not allowed.</li>
49773              * </ul>
49774              * @param {Object} dragOverEvent
49775              */
49776             'nodedragover'
49777         );
49778         if(this.singleExpand){
49779             this.on('beforeexpandnode', this.restrictExpand, this);
49780         }
49781     },
49782
49783     // private
49784     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){
49785         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){
49786             ename = ename+'node';
49787         }
49788         // args inline for performance while bubbling events
49789         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);
49790     },
49791
49792
49793     /**
49794      * Returns this root node for this tree
49795      * @return {Node}
49796      */
49797     getRootNode : function(){
49798         return this.root;
49799     },
49800
49801     /**
49802      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the
49803      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.
49804      * @param {Node} node
49805      * @return {Node}
49806      */
49807     setRootNode : function(node){
49808         this.destroyRoot();
49809         if(!node.render){ // attributes passed
49810             node = this.loader.createNode(node);
49811         }
49812         this.root = node;
49813         node.ownerTree = this;
49814         node.isRoot = true;
49815         this.registerNode(node);
49816         if(!this.rootVisible){
49817             var uiP = node.attributes.uiProvider;
49818             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);
49819         }
49820         if(this.innerCt){
49821             this.clearInnerCt();
49822             this.renderRoot();
49823         }
49824         return node;
49825     },
49826     
49827     clearInnerCt : function(){
49828         this.innerCt.update('');    
49829     },
49830     
49831     // private
49832     renderRoot : function(){
49833         this.root.render();
49834         if(!this.rootVisible){
49835             this.root.renderChildren();
49836         }
49837     },
49838
49839     /**
49840      * Gets a node in this tree by its id
49841      * @param {String} id
49842      * @return {Node}
49843      */
49844     getNodeById : function(id){
49845         return this.nodeHash[id];
49846     },
49847
49848     // private
49849     registerNode : function(node){
49850         this.nodeHash[node.id] = node;
49851     },
49852
49853     // private
49854     unregisterNode : function(node){
49855         delete this.nodeHash[node.id];
49856     },
49857
49858     // private
49859     toString : function(){
49860         return '[Tree'+(this.id?' '+this.id:'')+']';
49861     },
49862
49863     // private
49864     restrictExpand : function(node){
49865         var p = node.parentNode;
49866         if(p){
49867             if(p.expandedChild && p.expandedChild.parentNode == p){
49868                 p.expandedChild.collapse();
49869             }
49870             p.expandedChild = node;
49871         }
49872     },
49873
49874     /**
49875      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. 'id')
49876      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
49877      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
49878      * @return {Array}
49879      */
49880     getChecked : function(a, startNode){
49881         startNode = startNode || this.root;
49882         var r = [];
49883         var f = function(){
49884             if(this.attributes.checked){
49885                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
49886             }
49887         };
49888         startNode.cascade(f);
49889         return r;
49890     },
49891
49892     /**
49893      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.
49894      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.
49895      */
49896     getLoader : function(){
49897         return this.loader;
49898     },
49899
49900     /**
49901      * Expand all nodes
49902      */
49903     expandAll : function(){
49904         this.root.expand(true);
49905     },
49906
49907     /**
49908      * Collapse all nodes
49909      */
49910     collapseAll : function(){
49911         this.root.collapse(true);
49912     },
49913
49914     /**
49915      * Returns the selection model used by this TreePanel.
49916      * @return {TreeSelectionModel} The selection model used by this TreePanel
49917      */
49918     getSelectionModel : function(){
49919         if(!this.selModel){
49920             this.selModel = new Ext.tree.DefaultSelectionModel();
49921         }
49922         return this.selModel;
49923     },
49924
49925     /**
49926      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
49927      * @param {String} path
49928      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
49929      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
49930      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
49931      */
49932     expandPath : function(path, attr, callback){
49933         attr = attr || 'id';
49934         var keys = path.split(this.pathSeparator);
49935         var curNode = this.root;
49936         if(curNode.attributes[attr] != keys[1]){ // invalid root
49937             if(callback){
49938                 callback(false, null);
49939             }
49940             return;
49941         }
49942         var index = 1;
49943         var f = function(){
49944             if(++index == keys.length){
49945                 if(callback){
49946                     callback(true, curNode);
49947                 }
49948                 return;
49949             }
49950             var c = curNode.findChild(attr, keys[index]);
49951             if(!c){
49952                 if(callback){
49953                     callback(false, curNode);
49954                 }
49955                 return;
49956             }
49957             curNode = c;
49958             c.expand(false, false, f);
49959         };
49960         curNode.expand(false, false, f);
49961     },
49962
49963     /**
49964      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
49965      * @param {String} path
49966      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
49967      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
49968      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
49969      */
49970     selectPath : function(path, attr, callback){
49971         attr = attr || 'id';
49972         var keys = path.split(this.pathSeparator),
49973             v = keys.pop();
49974         if(keys.length > 1){
49975             var f = function(success, node){
49976                 if(success && node){
49977                     var n = node.findChild(attr, v);
49978                     if(n){
49979                         n.select();
49980                         if(callback){
49981                             callback(true, n);
49982                         }
49983                     }else if(callback){
49984                         callback(false, n);
49985                     }
49986                 }else{
49987                     if(callback){
49988                         callback(false, n);
49989                     }
49990                 }
49991             };
49992             this.expandPath(keys.join(this.pathSeparator), attr, f);
49993         }else{
49994             this.root.select();
49995             if(callback){
49996                 callback(true, this.root);
49997             }
49998         }
49999     },
50000
50001     /**
50002      * Returns the underlying Element for this tree
50003      * @return {Ext.Element} The Element
50004      */
50005     getTreeEl : function(){
50006         return this.body;
50007     },
50008
50009     // private
50010     onRender : function(ct, position){
50011         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);
50012         this.el.addClass('x-tree');
50013         this.innerCt = this.body.createChild({tag:'ul',
50014                cls:'x-tree-root-ct ' +
50015                (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')});
50016     },
50017
50018     // private
50019     initEvents : function(){
50020         Ext.tree.TreePanel.superclass.initEvents.call(this);
50021
50022         if(this.containerScroll){
50023             Ext.dd.ScrollManager.register(this.body);
50024         }
50025         if((this.enableDD || this.enableDrop) && !this.dropZone){
50026            /**
50027             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})
50028             * @property dropZone
50029             * @type Ext.tree.TreeDropZone
50030             */
50031              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {
50032                ddGroup: this.ddGroup || 'TreeDD', appendOnly: this.ddAppendOnly === true
50033            });
50034         }
50035         if((this.enableDD || this.enableDrag) && !this.dragZone){
50036            /**
50037             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})
50038             * @property dragZone
50039             * @type Ext.tree.TreeDragZone
50040             */
50041             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {
50042                ddGroup: this.ddGroup || 'TreeDD',
50043                scroll: this.ddScroll
50044            });
50045         }
50046         this.getSelectionModel().init(this);
50047     },
50048
50049     // private
50050     afterRender : function(){
50051         Ext.tree.TreePanel.superclass.afterRender.call(this);
50052         this.renderRoot();
50053     },
50054
50055     beforeDestroy : function(){
50056         if(this.rendered){
50057             Ext.dd.ScrollManager.unregister(this.body);
50058             Ext.destroy(this.dropZone, this.dragZone);
50059         }
50060         this.destroyRoot();
50061         Ext.destroy(this.loader);
50062         this.nodeHash = this.root = this.loader = null;
50063         Ext.tree.TreePanel.superclass.beforeDestroy.call(this);
50064     },
50065     
50066     /**
50067      * Destroy the root node. Not included by itself because we need to pass the silent parameter.
50068      * @private
50069      */
50070     destroyRoot : function(){
50071         if(this.root && this.root.destroy){
50072             this.root.destroy(true);
50073         }
50074     }
50075
50076     /**
50077      * @cfg {String/Number} activeItem
50078      * @hide
50079      */
50080     /**
50081      * @cfg {Boolean} autoDestroy
50082      * @hide
50083      */
50084     /**
50085      * @cfg {Object/String/Function} autoLoad
50086      * @hide
50087      */
50088     /**
50089      * @cfg {Boolean} autoWidth
50090      * @hide
50091      */
50092     /**
50093      * @cfg {Boolean/Number} bufferResize
50094      * @hide
50095      */
50096     /**
50097      * @cfg {String} defaultType
50098      * @hide
50099      */
50100     /**
50101      * @cfg {Object} defaults
50102      * @hide
50103      */
50104     /**
50105      * @cfg {Boolean} hideBorders
50106      * @hide
50107      */
50108     /**
50109      * @cfg {Mixed} items
50110      * @hide
50111      */
50112     /**
50113      * @cfg {String} layout
50114      * @hide
50115      */
50116     /**
50117      * @cfg {Object} layoutConfig
50118      * @hide
50119      */
50120     /**
50121      * @cfg {Boolean} monitorResize
50122      * @hide
50123      */
50124     /**
50125      * @property items
50126      * @hide
50127      */
50128     /**
50129      * @method cascade
50130      * @hide
50131      */
50132     /**
50133      * @method doLayout
50134      * @hide
50135      */
50136     /**
50137      * @method find
50138      * @hide
50139      */
50140     /**
50141      * @method findBy
50142      * @hide
50143      */
50144     /**
50145      * @method findById
50146      * @hide
50147      */
50148     /**
50149      * @method findByType
50150      * @hide
50151      */
50152     /**
50153      * @method getComponent
50154      * @hide
50155      */
50156     /**
50157      * @method getLayout
50158      * @hide
50159      */
50160     /**
50161      * @method getUpdater
50162      * @hide
50163      */
50164     /**
50165      * @method insert
50166      * @hide
50167      */
50168     /**
50169      * @method load
50170      * @hide
50171      */
50172     /**
50173      * @method remove
50174      * @hide
50175      */
50176     /**
50177      * @event add
50178      * @hide
50179      */
50180     /**
50181      * @method removeAll
50182      * @hide
50183      */
50184     /**
50185      * @event afterLayout
50186      * @hide
50187      */
50188     /**
50189      * @event beforeadd
50190      * @hide
50191      */
50192     /**
50193      * @event beforeremove
50194      * @hide
50195      */
50196     /**
50197      * @event remove
50198      * @hide
50199      */
50200
50201
50202
50203     /**
50204      * @cfg {String} allowDomMove  @hide
50205      */
50206     /**
50207      * @cfg {String} autoEl @hide
50208      */
50209     /**
50210      * @cfg {String} applyTo  @hide
50211      */
50212     /**
50213      * @cfg {String} contentEl  @hide
50214      */
50215     /**
50216      * @cfg {Mixed} data  @hide
50217      */
50218     /**
50219      * @cfg {Mixed} tpl  @hide
50220      */
50221     /**
50222      * @cfg {String} tplWriteMode  @hide
50223      */
50224     /**
50225      * @cfg {String} disabledClass  @hide
50226      */
50227     /**
50228      * @cfg {String} elements  @hide
50229      */
50230     /**
50231      * @cfg {String} html  @hide
50232      */
50233     /**
50234      * @cfg {Boolean} preventBodyReset
50235      * @hide
50236      */
50237     /**
50238      * @property disabled
50239      * @hide
50240      */
50241     /**
50242      * @method applyToMarkup
50243      * @hide
50244      */
50245     /**
50246      * @method enable
50247      * @hide
50248      */
50249     /**
50250      * @method disable
50251      * @hide
50252      */
50253     /**
50254      * @method setDisabled
50255      * @hide
50256      */
50257 });
50258
50259 Ext.tree.TreePanel.nodeTypes = {};
50260
50261 Ext.reg('treepanel', Ext.tree.TreePanel);Ext.tree.TreeEventModel = function(tree){
50262     this.tree = tree;
50263     this.tree.on('render', this.initEvents, this);
50264 };
50265
50266 Ext.tree.TreeEventModel.prototype = {
50267     initEvents : function(){
50268         var t = this.tree;
50269
50270         if(t.trackMouseOver !== false){
50271             t.mon(t.innerCt, {
50272                 scope: this,
50273                 mouseover: this.delegateOver,
50274                 mouseout: this.delegateOut
50275             });
50276         }
50277         t.mon(t.getTreeEl(), {
50278             scope: this,
50279             click: this.delegateClick,
50280             dblclick: this.delegateDblClick,
50281             contextmenu: this.delegateContextMenu
50282         });
50283     },
50284
50285     getNode : function(e){
50286         var t;
50287         if(t = e.getTarget('.x-tree-node-el', 10)){
50288             var id = Ext.fly(t, '_treeEvents').getAttribute('tree-node-id', 'ext');
50289             if(id){
50290                 return this.tree.getNodeById(id);
50291             }
50292         }
50293         return null;
50294     },
50295
50296     getNodeTarget : function(e){
50297         var t = e.getTarget('.x-tree-node-icon', 1);
50298         if(!t){
50299             t = e.getTarget('.x-tree-node-el', 6);
50300         }
50301         return t;
50302     },
50303
50304     delegateOut : function(e, t){
50305         if(!this.beforeEvent(e)){
50306             return;
50307         }
50308         if(e.getTarget('.x-tree-ec-icon', 1)){
50309             var n = this.getNode(e);
50310             this.onIconOut(e, n);
50311             if(n == this.lastEcOver){
50312                 delete this.lastEcOver;
50313             }
50314         }
50315         if((t = this.getNodeTarget(e)) && !e.within(t, true)){
50316             this.onNodeOut(e, this.getNode(e));
50317         }
50318     },
50319
50320     delegateOver : function(e, t){
50321         if(!this.beforeEvent(e)){
50322             return;
50323         }
50324         if(Ext.isGecko && !this.trackingDoc){ // prevent hanging in FF
50325             Ext.getBody().on('mouseover', this.trackExit, this);
50326             this.trackingDoc = true;
50327         }
50328         if(this.lastEcOver){ // prevent hung highlight
50329             this.onIconOut(e, this.lastEcOver);
50330             delete this.lastEcOver;
50331         }
50332         if(e.getTarget('.x-tree-ec-icon', 1)){
50333             this.lastEcOver = this.getNode(e);
50334             this.onIconOver(e, this.lastEcOver);
50335         }
50336         if(t = this.getNodeTarget(e)){
50337             this.onNodeOver(e, this.getNode(e));
50338         }
50339     },
50340
50341     trackExit : function(e){
50342         if(this.lastOverNode){
50343             if(this.lastOverNode.ui && !e.within(this.lastOverNode.ui.getEl())){
50344                 this.onNodeOut(e, this.lastOverNode);
50345             }
50346             delete this.lastOverNode;
50347             Ext.getBody().un('mouseover', this.trackExit, this);
50348             this.trackingDoc = false;
50349         }
50350
50351     },
50352
50353     delegateClick : function(e, t){
50354         if(this.beforeEvent(e)){
50355             if(e.getTarget('input[type=checkbox]', 1)){
50356                 this.onCheckboxClick(e, this.getNode(e));
50357             }else if(e.getTarget('.x-tree-ec-icon', 1)){
50358                 this.onIconClick(e, this.getNode(e));
50359             }else if(this.getNodeTarget(e)){
50360                 this.onNodeClick(e, this.getNode(e));
50361             }
50362         }else{
50363             this.checkContainerEvent(e, 'click');
50364         }
50365     },
50366
50367     delegateDblClick : function(e, t){
50368         if(this.beforeEvent(e)){
50369             if(this.getNodeTarget(e)){
50370                 this.onNodeDblClick(e, this.getNode(e));
50371             }
50372         }else{
50373             this.checkContainerEvent(e, 'dblclick');
50374         }
50375     },
50376
50377     delegateContextMenu : function(e, t){
50378         if(this.beforeEvent(e)){
50379             if(this.getNodeTarget(e)){
50380                 this.onNodeContextMenu(e, this.getNode(e));
50381             }
50382         }else{
50383             this.checkContainerEvent(e, 'contextmenu');
50384         }
50385     },
50386     
50387     checkContainerEvent: function(e, type){
50388         if(this.disabled){
50389             e.stopEvent();
50390             return false;
50391         }
50392         this.onContainerEvent(e, type);    
50393     },
50394
50395     onContainerEvent: function(e, type){
50396         this.tree.fireEvent('container' + type, this.tree, e);
50397     },
50398
50399     onNodeClick : function(e, node){
50400         node.ui.onClick(e);
50401     },
50402
50403     onNodeOver : function(e, node){
50404         this.lastOverNode = node;
50405         node.ui.onOver(e);
50406     },
50407
50408     onNodeOut : function(e, node){
50409         node.ui.onOut(e);
50410     },
50411
50412     onIconOver : function(e, node){
50413         node.ui.addClass('x-tree-ec-over');
50414     },
50415
50416     onIconOut : function(e, node){
50417         node.ui.removeClass('x-tree-ec-over');
50418     },
50419
50420     onIconClick : function(e, node){
50421         node.ui.ecClick(e);
50422     },
50423
50424     onCheckboxClick : function(e, node){
50425         node.ui.onCheckChange(e);
50426     },
50427
50428     onNodeDblClick : function(e, node){
50429         node.ui.onDblClick(e);
50430     },
50431
50432     onNodeContextMenu : function(e, node){
50433         node.ui.onContextMenu(e);
50434     },
50435
50436     beforeEvent : function(e){
50437         var node = this.getNode(e);
50438         if(this.disabled || !node || !node.ui){
50439             e.stopEvent();
50440             return false;
50441         }
50442         return true;
50443     },
50444
50445     disable: function(){
50446         this.disabled = true;
50447     },
50448
50449     enable: function(){
50450         this.disabled = false;
50451     }
50452 };/**
50453  * @class Ext.tree.DefaultSelectionModel
50454  * @extends Ext.util.Observable
50455  * The default single selection for a TreePanel.
50456  */
50457 Ext.tree.DefaultSelectionModel = function(config){
50458    this.selNode = null;
50459    
50460    this.addEvents(
50461        /**
50462         * @event selectionchange
50463         * Fires when the selected node changes
50464         * @param {DefaultSelectionModel} this
50465         * @param {TreeNode} node the new selection
50466         */
50467        'selectionchange',
50468
50469        /**
50470         * @event beforeselect
50471         * Fires before the selected node changes, return false to cancel the change
50472         * @param {DefaultSelectionModel} this
50473         * @param {TreeNode} node the new selection
50474         * @param {TreeNode} node the old selection
50475         */
50476        'beforeselect'
50477    );
50478
50479     Ext.apply(this, config);
50480     Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);
50481 };
50482
50483 Ext.extend(Ext.tree.DefaultSelectionModel, Ext.util.Observable, {
50484     init : function(tree){
50485         this.tree = tree;
50486         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
50487         tree.on('click', this.onNodeClick, this);
50488     },
50489     
50490     onNodeClick : function(node, e){
50491         this.select(node);
50492     },
50493     
50494     /**
50495      * Select a node.
50496      * @param {TreeNode} node The node to select
50497      * @return {TreeNode} The selected node
50498      */
50499     select : function(node, /* private*/ selectNextNode){
50500         // If node is hidden, select the next node in whatever direction was being moved in.
50501         if (!Ext.fly(node.ui.wrap).isVisible() && selectNextNode) {
50502             return selectNextNode.call(this, node);
50503         }
50504         var last = this.selNode;
50505         if(node == last){
50506             node.ui.onSelectedChange(true);
50507         }else if(this.fireEvent('beforeselect', this, node, last) !== false){
50508             if(last && last.ui){
50509                 last.ui.onSelectedChange(false);
50510             }
50511             this.selNode = node;
50512             node.ui.onSelectedChange(true);
50513             this.fireEvent('selectionchange', this, node, last);
50514         }
50515         return node;
50516     },
50517     
50518     /**
50519      * Deselect a node.
50520      * @param {TreeNode} node The node to unselect
50521      * @param {Boolean} silent True to stop the selectionchange event from firing.
50522      */
50523     unselect : function(node, silent){
50524         if(this.selNode == node){
50525             this.clearSelections(silent);
50526         }    
50527     },
50528     
50529     /**
50530      * Clear all selections
50531      * @param {Boolean} silent True to stop the selectionchange event from firing.
50532      */
50533     clearSelections : function(silent){
50534         var n = this.selNode;
50535         if(n){
50536             n.ui.onSelectedChange(false);
50537             this.selNode = null;
50538             if(silent !== true){
50539                 this.fireEvent('selectionchange', this, null);
50540             }
50541         }
50542         return n;
50543     },
50544     
50545     /**
50546      * Get the selected node
50547      * @return {TreeNode} The selected node
50548      */
50549     getSelectedNode : function(){
50550         return this.selNode;    
50551     },
50552     
50553     /**
50554      * Returns true if the node is selected
50555      * @param {TreeNode} node The node to check
50556      * @return {Boolean}
50557      */
50558     isSelected : function(node){
50559         return this.selNode == node;  
50560     },
50561
50562     /**
50563      * Selects the node above the selected node in the tree, intelligently walking the nodes
50564      * @return TreeNode The new selection
50565      */
50566     selectPrevious : function(/* private */ s){
50567         if(!(s = s || this.selNode || this.lastSelNode)){
50568             return null;
50569         }
50570         // Here we pass in the current function to select to indicate the direction we're moving
50571         var ps = s.previousSibling;
50572         if(ps){
50573             if(!ps.isExpanded() || ps.childNodes.length < 1){
50574                 return this.select(ps, this.selectPrevious);
50575             } else{
50576                 var lc = ps.lastChild;
50577                 while(lc && lc.isExpanded() && Ext.fly(lc.ui.wrap).isVisible() && lc.childNodes.length > 0){
50578                     lc = lc.lastChild;
50579                 }
50580                 return this.select(lc, this.selectPrevious);
50581             }
50582         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
50583             return this.select(s.parentNode, this.selectPrevious);
50584         }
50585         return null;
50586     },
50587
50588     /**
50589      * Selects the node above the selected node in the tree, intelligently walking the nodes
50590      * @return TreeNode The new selection
50591      */
50592     selectNext : function(/* private */ s){
50593         if(!(s = s || this.selNode || this.lastSelNode)){
50594             return null;
50595         }
50596         // Here we pass in the current function to select to indicate the direction we're moving
50597         if(s.firstChild && s.isExpanded() && Ext.fly(s.ui.wrap).isVisible()){
50598              return this.select(s.firstChild, this.selectNext);
50599          }else if(s.nextSibling){
50600              return this.select(s.nextSibling, this.selectNext);
50601          }else if(s.parentNode){
50602             var newS = null;
50603             s.parentNode.bubble(function(){
50604                 if(this.nextSibling){
50605                     newS = this.getOwnerTree().selModel.select(this.nextSibling, this.selectNext);
50606                     return false;
50607                 }
50608             });
50609             return newS;
50610          }
50611         return null;
50612     },
50613
50614     onKeyDown : function(e){
50615         var s = this.selNode || this.lastSelNode;
50616         // undesirable, but required
50617         var sm = this;
50618         if(!s){
50619             return;
50620         }
50621         var k = e.getKey();
50622         switch(k){
50623              case e.DOWN:
50624                  e.stopEvent();
50625                  this.selectNext();
50626              break;
50627              case e.UP:
50628                  e.stopEvent();
50629                  this.selectPrevious();
50630              break;
50631              case e.RIGHT:
50632                  e.preventDefault();
50633                  if(s.hasChildNodes()){
50634                      if(!s.isExpanded()){
50635                          s.expand();
50636                      }else if(s.firstChild){
50637                          this.select(s.firstChild, e);
50638                      }
50639                  }
50640              break;
50641              case e.LEFT:
50642                  e.preventDefault();
50643                  if(s.hasChildNodes() && s.isExpanded()){
50644                      s.collapse();
50645                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
50646                      this.select(s.parentNode, e);
50647                  }
50648              break;
50649         };
50650     }
50651 });
50652
50653 /**
50654  * @class Ext.tree.MultiSelectionModel
50655  * @extends Ext.util.Observable
50656  * Multi selection for a TreePanel.
50657  */
50658 Ext.tree.MultiSelectionModel = function(config){
50659    this.selNodes = [];
50660    this.selMap = {};
50661    this.addEvents(
50662        /**
50663         * @event selectionchange
50664         * Fires when the selected nodes change
50665         * @param {MultiSelectionModel} this
50666         * @param {Array} nodes Array of the selected nodes
50667         */
50668        'selectionchange'
50669    );
50670     Ext.apply(this, config);
50671     Ext.tree.MultiSelectionModel.superclass.constructor.call(this);
50672 };
50673
50674 Ext.extend(Ext.tree.MultiSelectionModel, Ext.util.Observable, {
50675     init : function(tree){
50676         this.tree = tree;
50677         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
50678         tree.on('click', this.onNodeClick, this);
50679     },
50680     
50681     onNodeClick : function(node, e){
50682         if(e.ctrlKey && this.isSelected(node)){
50683             this.unselect(node);
50684         }else{
50685             this.select(node, e, e.ctrlKey);
50686         }
50687     },
50688     
50689     /**
50690      * Select a node.
50691      * @param {TreeNode} node The node to select
50692      * @param {EventObject} e (optional) An event associated with the selection
50693      * @param {Boolean} keepExisting True to retain existing selections
50694      * @return {TreeNode} The selected node
50695      */
50696     select : function(node, e, keepExisting){
50697         if(keepExisting !== true){
50698             this.clearSelections(true);
50699         }
50700         if(this.isSelected(node)){
50701             this.lastSelNode = node;
50702             return node;
50703         }
50704         this.selNodes.push(node);
50705         this.selMap[node.id] = node;
50706         this.lastSelNode = node;
50707         node.ui.onSelectedChange(true);
50708         this.fireEvent('selectionchange', this, this.selNodes);
50709         return node;
50710     },
50711     
50712     /**
50713      * Deselect a node.
50714      * @param {TreeNode} node The node to unselect
50715      */
50716     unselect : function(node){
50717         if(this.selMap[node.id]){
50718             node.ui.onSelectedChange(false);
50719             var sn = this.selNodes;
50720             var index = sn.indexOf(node);
50721             if(index != -1){
50722                 this.selNodes.splice(index, 1);
50723             }
50724             delete this.selMap[node.id];
50725             this.fireEvent('selectionchange', this, this.selNodes);
50726         }
50727     },
50728     
50729     /**
50730      * Clear all selections
50731      */
50732     clearSelections : function(suppressEvent){
50733         var sn = this.selNodes;
50734         if(sn.length > 0){
50735             for(var i = 0, len = sn.length; i < len; i++){
50736                 sn[i].ui.onSelectedChange(false);
50737             }
50738             this.selNodes = [];
50739             this.selMap = {};
50740             if(suppressEvent !== true){
50741                 this.fireEvent('selectionchange', this, this.selNodes);
50742             }
50743         }
50744     },
50745     
50746     /**
50747      * Returns true if the node is selected
50748      * @param {TreeNode} node The node to check
50749      * @return {Boolean}
50750      */
50751     isSelected : function(node){
50752         return this.selMap[node.id] ? true : false;  
50753     },
50754     
50755     /**
50756      * Returns an array of the selected nodes
50757      * @return {Array}
50758      */
50759     getSelectedNodes : function(){
50760         return this.selNodes;    
50761     },
50762
50763     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,
50764
50765     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,
50766
50767     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious
50768 });/**
50769  * @class Ext.data.Tree
50770  * @extends Ext.util.Observable
50771  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
50772  * in the tree have most standard DOM functionality.
50773  * @constructor
50774  * @param {Node} root (optional) The root node
50775  */
50776 Ext.data.Tree = function(root){
50777    this.nodeHash = {};
50778    /**
50779     * The root node for this tree
50780     * @type Node
50781     */
50782    this.root = null;
50783    if(root){
50784        this.setRootNode(root);
50785    }
50786    this.addEvents(
50787        /**
50788         * @event append
50789         * Fires when a new child node is appended to a node in this tree.
50790         * @param {Tree} tree The owner tree
50791         * @param {Node} parent The parent node
50792         * @param {Node} node The newly appended node
50793         * @param {Number} index The index of the newly appended node
50794         */
50795        "append",
50796        /**
50797         * @event remove
50798         * Fires when a child node is removed from a node in this tree.
50799         * @param {Tree} tree The owner tree
50800         * @param {Node} parent The parent node
50801         * @param {Node} node The child node removed
50802         */
50803        "remove",
50804        /**
50805         * @event move
50806         * Fires when a node is moved to a new location in the tree
50807         * @param {Tree} tree The owner tree
50808         * @param {Node} node The node moved
50809         * @param {Node} oldParent The old parent of this node
50810         * @param {Node} newParent The new parent of this node
50811         * @param {Number} index The index it was moved to
50812         */
50813        "move",
50814        /**
50815         * @event insert
50816         * Fires when a new child node is inserted in a node in this tree.
50817         * @param {Tree} tree The owner tree
50818         * @param {Node} parent The parent node
50819         * @param {Node} node The child node inserted
50820         * @param {Node} refNode The child node the node was inserted before
50821         */
50822        "insert",
50823        /**
50824         * @event beforeappend
50825         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
50826         * @param {Tree} tree The owner tree
50827         * @param {Node} parent The parent node
50828         * @param {Node} node The child node to be appended
50829         */
50830        "beforeappend",
50831        /**
50832         * @event beforeremove
50833         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
50834         * @param {Tree} tree The owner tree
50835         * @param {Node} parent The parent node
50836         * @param {Node} node The child node to be removed
50837         */
50838        "beforeremove",
50839        /**
50840         * @event beforemove
50841         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
50842         * @param {Tree} tree The owner tree
50843         * @param {Node} node The node being moved
50844         * @param {Node} oldParent The parent of the node
50845         * @param {Node} newParent The new parent the node is moving to
50846         * @param {Number} index The index it is being moved to
50847         */
50848        "beforemove",
50849        /**
50850         * @event beforeinsert
50851         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
50852         * @param {Tree} tree The owner tree
50853         * @param {Node} parent The parent node
50854         * @param {Node} node The child node to be inserted
50855         * @param {Node} refNode The child node the node is being inserted before
50856         */
50857        "beforeinsert"
50858    );
50859
50860     Ext.data.Tree.superclass.constructor.call(this);
50861 };
50862
50863 Ext.extend(Ext.data.Tree, Ext.util.Observable, {
50864     /**
50865      * @cfg {String} pathSeparator
50866      * The token used to separate paths in node ids (defaults to '/').
50867      */
50868     pathSeparator: "/",
50869
50870     // private
50871     proxyNodeEvent : function(){
50872         return this.fireEvent.apply(this, arguments);
50873     },
50874
50875     /**
50876      * Returns the root node for this tree.
50877      * @return {Node}
50878      */
50879     getRootNode : function(){
50880         return this.root;
50881     },
50882
50883     /**
50884      * Sets the root node for this tree.
50885      * @param {Node} node
50886      * @return {Node}
50887      */
50888     setRootNode : function(node){
50889         this.root = node;
50890         node.ownerTree = this;
50891         node.isRoot = true;
50892         this.registerNode(node);
50893         return node;
50894     },
50895
50896     /**
50897      * Gets a node in this tree by its id.
50898      * @param {String} id
50899      * @return {Node}
50900      */
50901     getNodeById : function(id){
50902         return this.nodeHash[id];
50903     },
50904
50905     // private
50906     registerNode : function(node){
50907         this.nodeHash[node.id] = node;
50908     },
50909
50910     // private
50911     unregisterNode : function(node){
50912         delete this.nodeHash[node.id];
50913     },
50914
50915     toString : function(){
50916         return "[Tree"+(this.id?" "+this.id:"")+"]";
50917     }
50918 });
50919
50920 /**
50921  * @class Ext.data.Node
50922  * @extends Ext.util.Observable
50923  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
50924  * @cfg {String} id The id for this node. If one is not specified, one is generated.
50925  * @constructor
50926  * @param {Object} attributes The attributes/config for the node
50927  */
50928 Ext.data.Node = function(attributes){
50929     /**
50930      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
50931      * @type {Object}
50932      */
50933     this.attributes = attributes || {};
50934     this.leaf = this.attributes.leaf;
50935     /**
50936      * The node id. @type String
50937      */
50938     this.id = this.attributes.id;
50939     if(!this.id){
50940         this.id = Ext.id(null, "xnode-");
50941         this.attributes.id = this.id;
50942     }
50943     /**
50944      * All child nodes of this node. @type Array
50945      */
50946     this.childNodes = [];
50947     if(!this.childNodes.indexOf){ // indexOf is a must
50948         this.childNodes.indexOf = function(o){
50949             for(var i = 0, len = this.length; i < len; i++){
50950                 if(this[i] == o){
50951                     return i;
50952                 }
50953             }
50954             return -1;
50955         };
50956     }
50957     /**
50958      * The parent node for this node. @type Node
50959      */
50960     this.parentNode = null;
50961     /**
50962      * The first direct child node of this node, or null if this node has no child nodes. @type Node
50963      */
50964     this.firstChild = null;
50965     /**
50966      * The last direct child node of this node, or null if this node has no child nodes. @type Node
50967      */
50968     this.lastChild = null;
50969     /**
50970      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
50971      */
50972     this.previousSibling = null;
50973     /**
50974      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
50975      */
50976     this.nextSibling = null;
50977
50978     this.addEvents({
50979        /**
50980         * @event append
50981         * Fires when a new child node is appended
50982         * @param {Tree} tree The owner tree
50983         * @param {Node} this This node
50984         * @param {Node} node The newly appended node
50985         * @param {Number} index The index of the newly appended node
50986         */
50987        "append" : true,
50988        /**
50989         * @event remove
50990         * Fires when a child node is removed
50991         * @param {Tree} tree The owner tree
50992         * @param {Node} this This node
50993         * @param {Node} node The removed node
50994         */
50995        "remove" : true,
50996        /**
50997         * @event move
50998         * Fires when this node is moved to a new location in the tree
50999         * @param {Tree} tree The owner tree
51000         * @param {Node} this This node
51001         * @param {Node} oldParent The old parent of this node
51002         * @param {Node} newParent The new parent of this node
51003         * @param {Number} index The index it was moved to
51004         */
51005        "move" : true,
51006        /**
51007         * @event insert
51008         * Fires when a new child node is inserted.
51009         * @param {Tree} tree The owner tree
51010         * @param {Node} this This node
51011         * @param {Node} node The child node inserted
51012         * @param {Node} refNode The child node the node was inserted before
51013         */
51014        "insert" : true,
51015        /**
51016         * @event beforeappend
51017         * Fires before a new child is appended, return false to cancel the append.
51018         * @param {Tree} tree The owner tree
51019         * @param {Node} this This node
51020         * @param {Node} node The child node to be appended
51021         */
51022        "beforeappend" : true,
51023        /**
51024         * @event beforeremove
51025         * Fires before a child is removed, return false to cancel the remove.
51026         * @param {Tree} tree The owner tree
51027         * @param {Node} this This node
51028         * @param {Node} node The child node to be removed
51029         */
51030        "beforeremove" : true,
51031        /**
51032         * @event beforemove
51033         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
51034         * @param {Tree} tree The owner tree
51035         * @param {Node} this This node
51036         * @param {Node} oldParent The parent of this node
51037         * @param {Node} newParent The new parent this node is moving to
51038         * @param {Number} index The index it is being moved to
51039         */
51040        "beforemove" : true,
51041        /**
51042         * @event beforeinsert
51043         * Fires before a new child is inserted, return false to cancel the insert.
51044         * @param {Tree} tree The owner tree
51045         * @param {Node} this This node
51046         * @param {Node} node The child node to be inserted
51047         * @param {Node} refNode The child node the node is being inserted before
51048         */
51049        "beforeinsert" : true
51050    });
51051     this.listeners = this.attributes.listeners;
51052     Ext.data.Node.superclass.constructor.call(this);
51053 };
51054
51055 Ext.extend(Ext.data.Node, Ext.util.Observable, {
51056     // private
51057     fireEvent : function(evtName){
51058         // first do standard event for this node
51059         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
51060             return false;
51061         }
51062         // then bubble it up to the tree if the event wasn't cancelled
51063         var ot = this.getOwnerTree();
51064         if(ot){
51065             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
51066                 return false;
51067             }
51068         }
51069         return true;
51070     },
51071
51072     /**
51073      * Returns true if this node is a leaf
51074      * @return {Boolean}
51075      */
51076     isLeaf : function(){
51077         return this.leaf === true;
51078     },
51079
51080     // private
51081     setFirstChild : function(node){
51082         this.firstChild = node;
51083     },
51084
51085     //private
51086     setLastChild : function(node){
51087         this.lastChild = node;
51088     },
51089
51090
51091     /**
51092      * Returns true if this node is the last child of its parent
51093      * @return {Boolean}
51094      */
51095     isLast : function(){
51096        return (!this.parentNode ? true : this.parentNode.lastChild == this);
51097     },
51098
51099     /**
51100      * Returns true if this node is the first child of its parent
51101      * @return {Boolean}
51102      */
51103     isFirst : function(){
51104        return (!this.parentNode ? true : this.parentNode.firstChild == this);
51105     },
51106
51107     /**
51108      * Returns true if this node has one or more child nodes, else false.
51109      * @return {Boolean}
51110      */
51111     hasChildNodes : function(){
51112         return !this.isLeaf() && this.childNodes.length > 0;
51113     },
51114
51115     /**
51116      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
51117      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
51118      * @return {Boolean}
51119      */
51120     isExpandable : function(){
51121         return this.attributes.expandable || this.hasChildNodes();
51122     },
51123
51124     /**
51125      * Insert node(s) as the last child node of this node.
51126      * @param {Node/Array} node The node or Array of nodes to append
51127      * @return {Node} The appended node if single append, or null if an array was passed
51128      */
51129     appendChild : function(node){
51130         var multi = false;
51131         if(Ext.isArray(node)){
51132             multi = node;
51133         }else if(arguments.length > 1){
51134             multi = arguments;
51135         }
51136         // if passed an array or multiple args do them one by one
51137         if(multi){
51138             for(var i = 0, len = multi.length; i < len; i++) {
51139                 this.appendChild(multi[i]);
51140             }
51141         }else{
51142             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
51143                 return false;
51144             }
51145             var index = this.childNodes.length;
51146             var oldParent = node.parentNode;
51147             // it's a move, make sure we move it cleanly
51148             if(oldParent){
51149                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
51150                     return false;
51151                 }
51152                 oldParent.removeChild(node);
51153             }
51154             index = this.childNodes.length;
51155             if(index === 0){
51156                 this.setFirstChild(node);
51157             }
51158             this.childNodes.push(node);
51159             node.parentNode = this;
51160             var ps = this.childNodes[index-1];
51161             if(ps){
51162                 node.previousSibling = ps;
51163                 ps.nextSibling = node;
51164             }else{
51165                 node.previousSibling = null;
51166             }
51167             node.nextSibling = null;
51168             this.setLastChild(node);
51169             node.setOwnerTree(this.getOwnerTree());
51170             this.fireEvent("append", this.ownerTree, this, node, index);
51171             if(oldParent){
51172                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
51173             }
51174             return node;
51175         }
51176     },
51177
51178     /**
51179      * Removes a child node from this node.
51180      * @param {Node} node The node to remove
51181      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
51182      * @return {Node} The removed node
51183      */
51184     removeChild : function(node, destroy){
51185         var index = this.childNodes.indexOf(node);
51186         if(index == -1){
51187             return false;
51188         }
51189         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
51190             return false;
51191         }
51192
51193         // remove it from childNodes collection
51194         this.childNodes.splice(index, 1);
51195
51196         // update siblings
51197         if(node.previousSibling){
51198             node.previousSibling.nextSibling = node.nextSibling;
51199         }
51200         if(node.nextSibling){
51201             node.nextSibling.previousSibling = node.previousSibling;
51202         }
51203
51204         // update child refs
51205         if(this.firstChild == node){
51206             this.setFirstChild(node.nextSibling);
51207         }
51208         if(this.lastChild == node){
51209             this.setLastChild(node.previousSibling);
51210         }
51211
51212         this.fireEvent("remove", this.ownerTree, this, node);
51213         if(destroy){
51214             node.destroy(true);
51215         }else{
51216             node.clear();
51217         }
51218         return node;
51219     },
51220
51221     // private
51222     clear : function(destroy){
51223         // clear any references from the node
51224         this.setOwnerTree(null, destroy);
51225         this.parentNode = this.previousSibling = this.nextSibling = null;
51226         if(destroy){
51227             this.firstChild = this.lastChild = null;
51228         }
51229     },
51230
51231     /**
51232      * Destroys the node.
51233      */
51234     destroy : function(/* private */ silent){
51235         /*
51236          * Silent is to be used in a number of cases
51237          * 1) When setRootNode is called.
51238          * 2) When destroy on the tree is called
51239          * 3) For destroying child nodes on a node
51240          */
51241         if(silent === true){
51242             this.purgeListeners();
51243             this.clear(true);
51244             Ext.each(this.childNodes, function(n){
51245                 n.destroy(true);
51246             });
51247             this.childNodes = null;
51248         }else{
51249             this.remove(true);
51250         }
51251     },
51252
51253     /**
51254      * Inserts the first node before the second node in this nodes childNodes collection.
51255      * @param {Node} node The node to insert
51256      * @param {Node} refNode The node to insert before (if null the node is appended)
51257      * @return {Node} The inserted node
51258      */
51259     insertBefore : function(node, refNode){
51260         if(!refNode){ // like standard Dom, refNode can be null for append
51261             return this.appendChild(node);
51262         }
51263         // nothing to do
51264         if(node == refNode){
51265             return false;
51266         }
51267
51268         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
51269             return false;
51270         }
51271         var index = this.childNodes.indexOf(refNode);
51272         var oldParent = node.parentNode;
51273         var refIndex = index;
51274
51275         // when moving internally, indexes will change after remove
51276         if(oldParent == this && this.childNodes.indexOf(node) < index){
51277             refIndex--;
51278         }
51279
51280         // it's a move, make sure we move it cleanly
51281         if(oldParent){
51282             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
51283                 return false;
51284             }
51285             oldParent.removeChild(node);
51286         }
51287         if(refIndex === 0){
51288             this.setFirstChild(node);
51289         }
51290         this.childNodes.splice(refIndex, 0, node);
51291         node.parentNode = this;
51292         var ps = this.childNodes[refIndex-1];
51293         if(ps){
51294             node.previousSibling = ps;
51295             ps.nextSibling = node;
51296         }else{
51297             node.previousSibling = null;
51298         }
51299         node.nextSibling = refNode;
51300         refNode.previousSibling = node;
51301         node.setOwnerTree(this.getOwnerTree());
51302         this.fireEvent("insert", this.ownerTree, this, node, refNode);
51303         if(oldParent){
51304             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
51305         }
51306         return node;
51307     },
51308
51309     /**
51310      * Removes this node from its parent
51311      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
51312      * @return {Node} this
51313      */
51314     remove : function(destroy){
51315         if (this.parentNode) {
51316             this.parentNode.removeChild(this, destroy);
51317         }
51318         return this;
51319     },
51320
51321     /**
51322      * Removes all child nodes from this node.
51323      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
51324      * @return {Node} this
51325      */
51326     removeAll : function(destroy){
51327         var cn = this.childNodes,
51328             n;
51329         while((n = cn[0])){
51330             this.removeChild(n, destroy);
51331         }
51332         return this;
51333     },
51334
51335     /**
51336      * Returns the child node at the specified index.
51337      * @param {Number} index
51338      * @return {Node}
51339      */
51340     item : function(index){
51341         return this.childNodes[index];
51342     },
51343
51344     /**
51345      * Replaces one child node in this node with another.
51346      * @param {Node} newChild The replacement node
51347      * @param {Node} oldChild The node to replace
51348      * @return {Node} The replaced node
51349      */
51350     replaceChild : function(newChild, oldChild){
51351         var s = oldChild ? oldChild.nextSibling : null;
51352         this.removeChild(oldChild);
51353         this.insertBefore(newChild, s);
51354         return oldChild;
51355     },
51356
51357     /**
51358      * Returns the index of a child node
51359      * @param {Node} node
51360      * @return {Number} The index of the node or -1 if it was not found
51361      */
51362     indexOf : function(child){
51363         return this.childNodes.indexOf(child);
51364     },
51365
51366     /**
51367      * Returns the tree this node is in.
51368      * @return {Tree}
51369      */
51370     getOwnerTree : function(){
51371         // if it doesn't have one, look for one
51372         if(!this.ownerTree){
51373             var p = this;
51374             while(p){
51375                 if(p.ownerTree){
51376                     this.ownerTree = p.ownerTree;
51377                     break;
51378                 }
51379                 p = p.parentNode;
51380             }
51381         }
51382         return this.ownerTree;
51383     },
51384
51385     /**
51386      * Returns depth of this node (the root node has a depth of 0)
51387      * @return {Number}
51388      */
51389     getDepth : function(){
51390         var depth = 0;
51391         var p = this;
51392         while(p.parentNode){
51393             ++depth;
51394             p = p.parentNode;
51395         }
51396         return depth;
51397     },
51398
51399     // private
51400     setOwnerTree : function(tree, destroy){
51401         // if it is a move, we need to update everyone
51402         if(tree != this.ownerTree){
51403             if(this.ownerTree){
51404                 this.ownerTree.unregisterNode(this);
51405             }
51406             this.ownerTree = tree;
51407             // If we're destroying, we don't need to recurse since it will be called on each child node
51408             if(destroy !== true){
51409                 Ext.each(this.childNodes, function(n){
51410                     n.setOwnerTree(tree);
51411                 });
51412             }
51413             if(tree){
51414                 tree.registerNode(this);
51415             }
51416         }
51417     },
51418
51419     /**
51420      * Changes the id of this node.
51421      * @param {String} id The new id for the node.
51422      */
51423     setId: function(id){
51424         if(id !== this.id){
51425             var t = this.ownerTree;
51426             if(t){
51427                 t.unregisterNode(this);
51428             }
51429             this.id = this.attributes.id = id;
51430             if(t){
51431                 t.registerNode(this);
51432             }
51433             this.onIdChange(id);
51434         }
51435     },
51436
51437     // private
51438     onIdChange: Ext.emptyFn,
51439
51440     /**
51441      * Returns the path for this node. The path can be used to expand or select this node programmatically.
51442      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
51443      * @return {String} The path
51444      */
51445     getPath : function(attr){
51446         attr = attr || "id";
51447         var p = this.parentNode;
51448         var b = [this.attributes[attr]];
51449         while(p){
51450             b.unshift(p.attributes[attr]);
51451             p = p.parentNode;
51452         }
51453         var sep = this.getOwnerTree().pathSeparator;
51454         return sep + b.join(sep);
51455     },
51456
51457     /**
51458      * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
51459      * will be the args provided or the current node. If the function returns false at any point,
51460      * the bubble is stopped.
51461      * @param {Function} fn The function to call
51462      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
51463      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
51464      */
51465     bubble : function(fn, scope, args){
51466         var p = this;
51467         while(p){
51468             if(fn.apply(scope || p, args || [p]) === false){
51469                 break;
51470             }
51471             p = p.parentNode;
51472         }
51473     },
51474
51475     /**
51476      * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
51477      * will be the args provided or the current node. If the function returns false at any point,
51478      * the cascade is stopped on that branch.
51479      * @param {Function} fn The function to call
51480      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
51481      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
51482      */
51483     cascade : function(fn, scope, args){
51484         if(fn.apply(scope || this, args || [this]) !== false){
51485             var cs = this.childNodes;
51486             for(var i = 0, len = cs.length; i < len; i++) {
51487                 cs[i].cascade(fn, scope, args);
51488             }
51489         }
51490     },
51491
51492     /**
51493      * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
51494      * will be the args provided or the current node. If the function returns false at any point,
51495      * the iteration stops.
51496      * @param {Function} fn The function to call
51497      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
51498      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
51499      */
51500     eachChild : function(fn, scope, args){
51501         var cs = this.childNodes;
51502         for(var i = 0, len = cs.length; i < len; i++) {
51503             if(fn.apply(scope || this, args || [cs[i]]) === false){
51504                 break;
51505             }
51506         }
51507     },
51508
51509     /**
51510      * Finds the first child that has the attribute with the specified value.
51511      * @param {String} attribute The attribute name
51512      * @param {Mixed} value The value to search for
51513      * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
51514      * @return {Node} The found child or null if none was found
51515      */
51516     findChild : function(attribute, value, deep){
51517         return this.findChildBy(function(){
51518             return this.attributes[attribute] == value;
51519         }, null, deep);
51520     },
51521
51522     /**
51523      * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
51524      * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
51525      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
51526      * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
51527      * @return {Node} The found child or null if none was found
51528      */
51529     findChildBy : function(fn, scope, deep){
51530         var cs = this.childNodes,
51531             len = cs.length,
51532             i = 0,
51533             n,
51534             res;
51535         for(; i < len; i++){
51536             n = cs[i];
51537             if(fn.call(scope || n, n) === true){
51538                 return n;
51539             }else if (deep){
51540                 res = n.findChildBy(fn, scope, deep);
51541                 if(res != null){
51542                     return res;
51543                 }
51544             }
51545             
51546         }
51547         return null;
51548     },
51549
51550     /**
51551      * Sorts this nodes children using the supplied sort function.
51552      * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
51553      * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
51554      */
51555     sort : function(fn, scope){
51556         var cs = this.childNodes;
51557         var len = cs.length;
51558         if(len > 0){
51559             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
51560             cs.sort(sortFn);
51561             for(var i = 0; i < len; i++){
51562                 var n = cs[i];
51563                 n.previousSibling = cs[i-1];
51564                 n.nextSibling = cs[i+1];
51565                 if(i === 0){
51566                     this.setFirstChild(n);
51567                 }
51568                 if(i == len-1){
51569                     this.setLastChild(n);
51570                 }
51571             }
51572         }
51573     },
51574
51575     /**
51576      * Returns true if this node is an ancestor (at any point) of the passed node.
51577      * @param {Node} node
51578      * @return {Boolean}
51579      */
51580     contains : function(node){
51581         return node.isAncestor(this);
51582     },
51583
51584     /**
51585      * Returns true if the passed node is an ancestor (at any point) of this node.
51586      * @param {Node} node
51587      * @return {Boolean}
51588      */
51589     isAncestor : function(node){
51590         var p = this.parentNode;
51591         while(p){
51592             if(p == node){
51593                 return true;
51594             }
51595             p = p.parentNode;
51596         }
51597         return false;
51598     },
51599
51600     toString : function(){
51601         return "[Node"+(this.id?" "+this.id:"")+"]";
51602     }
51603 });/**
51604  * @class Ext.tree.TreeNode
51605  * @extends Ext.data.Node
51606  * @cfg {String} text The text for this node
51607  * @cfg {Boolean} expanded true to start the node expanded
51608  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)
51609  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)
51610  * @cfg {Boolean} disabled true to start the node disabled
51611  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
51612  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
51613  * @cfg {String} cls A css class to be added to the node
51614  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
51615  * @cfg {String} href URL of the link used for the node (defaults to #)
51616  * @cfg {String} hrefTarget target frame for the link
51617  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).
51618  * @cfg {String} qtip An Ext QuickTip for the node
51619  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty
51620  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
51621  * @cfg {Boolean} singleClickExpand True for single click expand on this node
51622  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)
51623  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
51624  * (defaults to undefined with no checkbox rendered)
51625  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)
51626  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)
51627  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)
51628  * @cfg {Boolean} editable False to not allow this node to be edited by an {@link Ext.tree.TreeEditor} (defaults to true)
51629  * @constructor
51630  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
51631  */
51632 Ext.tree.TreeNode = function(attributes){
51633     attributes = attributes || {};
51634     if(Ext.isString(attributes)){
51635         attributes = {text: attributes};
51636     }
51637     this.childrenRendered = false;
51638     this.rendered = false;
51639     Ext.tree.TreeNode.superclass.constructor.call(this, attributes);
51640     this.expanded = attributes.expanded === true;
51641     this.isTarget = attributes.isTarget !== false;
51642     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
51643     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
51644
51645     /**
51646      * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.
51647      * @type String
51648      */
51649     this.text = attributes.text;
51650     /**
51651      * True if this node is disabled.
51652      * @type Boolean
51653      */
51654     this.disabled = attributes.disabled === true;
51655     /**
51656      * True if this node is hidden.
51657      * @type Boolean
51658      */
51659     this.hidden = attributes.hidden === true;
51660
51661     this.addEvents(
51662         /**
51663         * @event textchange
51664         * Fires when the text for this node is changed
51665         * @param {Node} this This node
51666         * @param {String} text The new text
51667         * @param {String} oldText The old text
51668         */
51669         'textchange',
51670         /**
51671         * @event beforeexpand
51672         * Fires before this node is expanded, return false to cancel.
51673         * @param {Node} this This node
51674         * @param {Boolean} deep
51675         * @param {Boolean} anim
51676         */
51677         'beforeexpand',
51678         /**
51679         * @event beforecollapse
51680         * Fires before this node is collapsed, return false to cancel.
51681         * @param {Node} this This node
51682         * @param {Boolean} deep
51683         * @param {Boolean} anim
51684         */
51685         'beforecollapse',
51686         /**
51687         * @event expand
51688         * Fires when this node is expanded
51689         * @param {Node} this This node
51690         */
51691         'expand',
51692         /**
51693         * @event disabledchange
51694         * Fires when the disabled status of this node changes
51695         * @param {Node} this This node
51696         * @param {Boolean} disabled
51697         */
51698         'disabledchange',
51699         /**
51700         * @event collapse
51701         * Fires when this node is collapsed
51702         * @param {Node} this This node
51703         */
51704         'collapse',
51705         /**
51706         * @event beforeclick
51707         * Fires before click processing. Return false to cancel the default action.
51708         * @param {Node} this This node
51709         * @param {Ext.EventObject} e The event object
51710         */
51711         'beforeclick',
51712         /**
51713         * @event click
51714         * Fires when this node is clicked
51715         * @param {Node} this This node
51716         * @param {Ext.EventObject} e The event object
51717         */
51718         'click',
51719         /**
51720         * @event checkchange
51721         * Fires when a node with a checkbox's checked property changes
51722         * @param {Node} this This node
51723         * @param {Boolean} checked
51724         */
51725         'checkchange',
51726         /**
51727         * @event beforedblclick
51728         * Fires before double click processing. Return false to cancel the default action.
51729         * @param {Node} this This node
51730         * @param {Ext.EventObject} e The event object
51731         */
51732         'beforedblclick',
51733         /**
51734         * @event dblclick
51735         * Fires when this node is double clicked
51736         * @param {Node} this This node
51737         * @param {Ext.EventObject} e The event object
51738         */
51739         'dblclick',
51740         /**
51741         * @event contextmenu
51742         * Fires when this node is right clicked
51743         * @param {Node} this This node
51744         * @param {Ext.EventObject} e The event object
51745         */
51746         'contextmenu',
51747         /**
51748         * @event beforechildrenrendered
51749         * Fires right before the child nodes for this node are rendered
51750         * @param {Node} this This node
51751         */
51752         'beforechildrenrendered'
51753     );
51754
51755     var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;
51756
51757     /**
51758      * Read-only. The UI for this node
51759      * @type TreeNodeUI
51760      */
51761     this.ui = new uiClass(this);
51762 };
51763 Ext.extend(Ext.tree.TreeNode, Ext.data.Node, {
51764     preventHScroll : true,
51765     /**
51766      * Returns true if this node is expanded
51767      * @return {Boolean}
51768      */
51769     isExpanded : function(){
51770         return this.expanded;
51771     },
51772
51773 /**
51774  * Returns the UI object for this node.
51775  * @return {TreeNodeUI} The object which is providing the user interface for this tree
51776  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance
51777  * of {@link Ext.tree.TreeNodeUI}
51778  */
51779     getUI : function(){
51780         return this.ui;
51781     },
51782
51783     getLoader : function(){
51784         var owner;
51785         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : (this.loader = new Ext.tree.TreeLoader()));
51786     },
51787
51788     // private override
51789     setFirstChild : function(node){
51790         var of = this.firstChild;
51791         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);
51792         if(this.childrenRendered && of && node != of){
51793             of.renderIndent(true, true);
51794         }
51795         if(this.rendered){
51796             this.renderIndent(true, true);
51797         }
51798     },
51799
51800     // private override
51801     setLastChild : function(node){
51802         var ol = this.lastChild;
51803         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);
51804         if(this.childrenRendered && ol && node != ol){
51805             ol.renderIndent(true, true);
51806         }
51807         if(this.rendered){
51808             this.renderIndent(true, true);
51809         }
51810     },
51811
51812     // these methods are overridden to provide lazy rendering support
51813     // private override
51814     appendChild : function(n){
51815         if(!n.render && !Ext.isArray(n)){
51816             n = this.getLoader().createNode(n);
51817         }
51818         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);
51819         if(node && this.childrenRendered){
51820             node.render();
51821         }
51822         this.ui.updateExpandIcon();
51823         return node;
51824     },
51825
51826     // private override
51827     removeChild : function(node, destroy){
51828         this.ownerTree.getSelectionModel().unselect(node);
51829         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);
51830         // only update the ui if we're not destroying
51831         if(!destroy){
51832             // if it's been rendered remove dom node
51833             if(node.ui.rendered){
51834                 node.ui.remove();
51835             }
51836             if(this.childNodes.length < 1){
51837                 this.collapse(false, false);
51838             }else{
51839                 this.ui.updateExpandIcon();
51840             }
51841             if(!this.firstChild && !this.isHiddenRoot()){
51842                 this.childrenRendered = false;
51843             }
51844         }
51845         return node;
51846     },
51847
51848     // private override
51849     insertBefore : function(node, refNode){
51850         if(!node.render){
51851             node = this.getLoader().createNode(node);
51852         }
51853         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);
51854         if(newNode && refNode && this.childrenRendered){
51855             node.render();
51856         }
51857         this.ui.updateExpandIcon();
51858         return newNode;
51859     },
51860
51861     /**
51862      * Sets the text for this node
51863      * @param {String} text
51864      */
51865     setText : function(text){
51866         var oldText = this.text;
51867         this.text = this.attributes.text = text;
51868         if(this.rendered){ // event without subscribing
51869             this.ui.onTextChange(this, text, oldText);
51870         }
51871         this.fireEvent('textchange', this, text, oldText);
51872     },
51873
51874     /**
51875      * Triggers selection of this node
51876      */
51877     select : function(){
51878         var t = this.getOwnerTree();
51879         if(t){
51880             t.getSelectionModel().select(this);
51881         }
51882     },
51883
51884     /**
51885      * Triggers deselection of this node
51886      * @param {Boolean} silent (optional) True to stop selection change events from firing.
51887      */
51888     unselect : function(silent){
51889         var t = this.getOwnerTree();
51890         if(t){
51891             t.getSelectionModel().unselect(this, silent);
51892         }
51893     },
51894
51895     /**
51896      * Returns true if this node is selected
51897      * @return {Boolean}
51898      */
51899     isSelected : function(){
51900         var t = this.getOwnerTree();
51901         return t ? t.getSelectionModel().isSelected(this) : false;
51902     },
51903
51904     /**
51905      * Expand this node.
51906      * @param {Boolean} deep (optional) True to expand all children as well
51907      * @param {Boolean} anim (optional) false to cancel the default animation
51908      * @param {Function} callback (optional) A callback to be called when
51909      * expanding this node completes (does not wait for deep expand to complete).
51910      * Called with 1 parameter, this node.
51911      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
51912      */
51913     expand : function(deep, anim, callback, scope){
51914         if(!this.expanded){
51915             if(this.fireEvent('beforeexpand', this, deep, anim) === false){
51916                 return;
51917             }
51918             if(!this.childrenRendered){
51919                 this.renderChildren();
51920             }
51921             this.expanded = true;
51922             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
51923                 this.ui.animExpand(function(){
51924                     this.fireEvent('expand', this);
51925                     this.runCallback(callback, scope || this, [this]);
51926                     if(deep === true){
51927                         this.expandChildNodes(true);
51928                     }
51929                 }.createDelegate(this));
51930                 return;
51931             }else{
51932                 this.ui.expand();
51933                 this.fireEvent('expand', this);
51934                 this.runCallback(callback, scope || this, [this]);
51935             }
51936         }else{
51937            this.runCallback(callback, scope || this, [this]);
51938         }
51939         if(deep === true){
51940             this.expandChildNodes(true);
51941         }
51942     },
51943
51944     runCallback : function(cb, scope, args){
51945         if(Ext.isFunction(cb)){
51946             cb.apply(scope, args);
51947         }
51948     },
51949
51950     isHiddenRoot : function(){
51951         return this.isRoot && !this.getOwnerTree().rootVisible;
51952     },
51953
51954     /**
51955      * Collapse this node.
51956      * @param {Boolean} deep (optional) True to collapse all children as well
51957      * @param {Boolean} anim (optional) false to cancel the default animation
51958      * @param {Function} callback (optional) A callback to be called when
51959      * expanding this node completes (does not wait for deep expand to complete).
51960      * Called with 1 parameter, this node.
51961      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
51962      */
51963     collapse : function(deep, anim, callback, scope){
51964         if(this.expanded && !this.isHiddenRoot()){
51965             if(this.fireEvent('beforecollapse', this, deep, anim) === false){
51966                 return;
51967             }
51968             this.expanded = false;
51969             if((this.getOwnerTree().animate && anim !== false) || anim){
51970                 this.ui.animCollapse(function(){
51971                     this.fireEvent('collapse', this);
51972                     this.runCallback(callback, scope || this, [this]);
51973                     if(deep === true){
51974                         this.collapseChildNodes(true);
51975                     }
51976                 }.createDelegate(this));
51977                 return;
51978             }else{
51979                 this.ui.collapse();
51980                 this.fireEvent('collapse', this);
51981                 this.runCallback(callback, scope || this, [this]);
51982             }
51983         }else if(!this.expanded){
51984             this.runCallback(callback, scope || this, [this]);
51985         }
51986         if(deep === true){
51987             var cs = this.childNodes;
51988             for(var i = 0, len = cs.length; i < len; i++) {
51989                 cs[i].collapse(true, false);
51990             }
51991         }
51992     },
51993
51994     // private
51995     delayedExpand : function(delay){
51996         if(!this.expandProcId){
51997             this.expandProcId = this.expand.defer(delay, this);
51998         }
51999     },
52000
52001     // private
52002     cancelExpand : function(){
52003         if(this.expandProcId){
52004             clearTimeout(this.expandProcId);
52005         }
52006         this.expandProcId = false;
52007     },
52008
52009     /**
52010      * Toggles expanded/collapsed state of the node
52011      */
52012     toggle : function(){
52013         if(this.expanded){
52014             this.collapse();
52015         }else{
52016             this.expand();
52017         }
52018     },
52019
52020     /**
52021      * Ensures all parent nodes are expanded, and if necessary, scrolls
52022      * the node into view.
52023      * @param {Function} callback (optional) A function to call when the node has been made visible.
52024      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
52025      */
52026     ensureVisible : function(callback, scope){
52027         var tree = this.getOwnerTree();
52028         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){
52029             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime
52030             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);
52031             this.runCallback(callback, scope || this, [this]);
52032         }.createDelegate(this));
52033     },
52034
52035     /**
52036      * Expand all child nodes
52037      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
52038      */
52039     expandChildNodes : function(deep){
52040         var cs = this.childNodes;
52041         for(var i = 0, len = cs.length; i < len; i++) {
52042                 cs[i].expand(deep);
52043         }
52044     },
52045
52046     /**
52047      * Collapse all child nodes
52048      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
52049      */
52050     collapseChildNodes : function(deep){
52051         var cs = this.childNodes;
52052         for(var i = 0, len = cs.length; i < len; i++) {
52053                 cs[i].collapse(deep);
52054         }
52055     },
52056
52057     /**
52058      * Disables this node
52059      */
52060     disable : function(){
52061         this.disabled = true;
52062         this.unselect();
52063         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
52064             this.ui.onDisableChange(this, true);
52065         }
52066         this.fireEvent('disabledchange', this, true);
52067     },
52068
52069     /**
52070      * Enables this node
52071      */
52072     enable : function(){
52073         this.disabled = false;
52074         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
52075             this.ui.onDisableChange(this, false);
52076         }
52077         this.fireEvent('disabledchange', this, false);
52078     },
52079
52080     // private
52081     renderChildren : function(suppressEvent){
52082         if(suppressEvent !== false){
52083             this.fireEvent('beforechildrenrendered', this);
52084         }
52085         var cs = this.childNodes;
52086         for(var i = 0, len = cs.length; i < len; i++){
52087             cs[i].render(true);
52088         }
52089         this.childrenRendered = true;
52090     },
52091
52092     // private
52093     sort : function(fn, scope){
52094         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);
52095         if(this.childrenRendered){
52096             var cs = this.childNodes;
52097             for(var i = 0, len = cs.length; i < len; i++){
52098                 cs[i].render(true);
52099             }
52100         }
52101     },
52102
52103     // private
52104     render : function(bulkRender){
52105         this.ui.render(bulkRender);
52106         if(!this.rendered){
52107             // make sure it is registered
52108             this.getOwnerTree().registerNode(this);
52109             this.rendered = true;
52110             if(this.expanded){
52111                 this.expanded = false;
52112                 this.expand(false, false);
52113             }
52114         }
52115     },
52116
52117     // private
52118     renderIndent : function(deep, refresh){
52119         if(refresh){
52120             this.ui.childIndent = null;
52121         }
52122         this.ui.renderIndent();
52123         if(deep === true && this.childrenRendered){
52124             var cs = this.childNodes;
52125             for(var i = 0, len = cs.length; i < len; i++){
52126                 cs[i].renderIndent(true, refresh);
52127             }
52128         }
52129     },
52130
52131     beginUpdate : function(){
52132         this.childrenRendered = false;
52133     },
52134
52135     endUpdate : function(){
52136         if(this.expanded && this.rendered){
52137             this.renderChildren();
52138         }
52139     },
52140
52141     //inherit docs
52142     destroy : function(silent){
52143         if(silent === true){
52144             this.unselect(true);
52145         }
52146         Ext.tree.TreeNode.superclass.destroy.call(this, silent);
52147         Ext.destroy(this.ui, this.loader);
52148         this.ui = this.loader = null;
52149     },
52150
52151     // private
52152     onIdChange : function(id){
52153         this.ui.onIdChange(id);
52154     }
52155 });
52156
52157 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;/**
52158  * @class Ext.tree.AsyncTreeNode
52159  * @extends Ext.tree.TreeNode
52160  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
52161  * @constructor
52162  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
52163  */
52164  Ext.tree.AsyncTreeNode = function(config){
52165     this.loaded = config && config.loaded === true;
52166     this.loading = false;
52167     Ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
52168     /**
52169     * @event beforeload
52170     * Fires before this node is loaded, return false to cancel
52171     * @param {Node} this This node
52172     */
52173     this.addEvents('beforeload', 'load');
52174     /**
52175     * @event load
52176     * Fires when this node is loaded
52177     * @param {Node} this This node
52178     */
52179     /**
52180      * The loader used by this node (defaults to using the tree's defined loader)
52181      * @type TreeLoader
52182      * @property loader
52183      */
52184 };
52185 Ext.extend(Ext.tree.AsyncTreeNode, Ext.tree.TreeNode, {
52186     expand : function(deep, anim, callback, scope){
52187         if(this.loading){ // if an async load is already running, waiting til it's done
52188             var timer;
52189             var f = function(){
52190                 if(!this.loading){ // done loading
52191                     clearInterval(timer);
52192                     this.expand(deep, anim, callback, scope);
52193                 }
52194             }.createDelegate(this);
52195             timer = setInterval(f, 200);
52196             return;
52197         }
52198         if(!this.loaded){
52199             if(this.fireEvent("beforeload", this) === false){
52200                 return;
52201             }
52202             this.loading = true;
52203             this.ui.beforeLoad(this);
52204             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
52205             if(loader){
52206                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback, scope]), this);
52207                 return;
52208             }
52209         }
52210         Ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback, scope);
52211     },
52212     
52213     /**
52214      * Returns true if this node is currently loading
52215      * @return {Boolean}
52216      */
52217     isLoading : function(){
52218         return this.loading;  
52219     },
52220     
52221     loadComplete : function(deep, anim, callback, scope){
52222         this.loading = false;
52223         this.loaded = true;
52224         this.ui.afterLoad(this);
52225         this.fireEvent("load", this);
52226         this.expand(deep, anim, callback, scope);
52227     },
52228     
52229     /**
52230      * Returns true if this node has been loaded
52231      * @return {Boolean}
52232      */
52233     isLoaded : function(){
52234         return this.loaded;
52235     },
52236     
52237     hasChildNodes : function(){
52238         if(!this.isLeaf() && !this.loaded){
52239             return true;
52240         }else{
52241             return Ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
52242         }
52243     },
52244
52245     /**
52246      * Trigger a reload for this node
52247      * @param {Function} callback
52248      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Node.
52249      */
52250     reload : function(callback, scope){
52251         this.collapse(false, false);
52252         while(this.firstChild){
52253             this.removeChild(this.firstChild).destroy();
52254         }
52255         this.childrenRendered = false;
52256         this.loaded = false;
52257         if(this.isHiddenRoot()){
52258             this.expanded = false;
52259         }
52260         this.expand(false, false, callback, scope);
52261     }
52262 });
52263
52264 Ext.tree.TreePanel.nodeTypes.async = Ext.tree.AsyncTreeNode;/**
52265  * @class Ext.tree.TreeNodeUI
52266  * This class provides the default UI implementation for Ext TreeNodes.
52267  * The TreeNode UI implementation is separate from the
52268  * tree implementation, and allows customizing of the appearance of
52269  * tree nodes.<br>
52270  * <p>
52271  * If you are customizing the Tree's user interface, you
52272  * may need to extend this class, but you should never need to instantiate this class.<br>
52273  * <p>
52274  * This class provides access to the user interface components of an Ext TreeNode, through
52275  * {@link Ext.tree.TreeNode#getUI}
52276  */
52277 Ext.tree.TreeNodeUI = function(node){
52278     this.node = node;
52279     this.rendered = false;
52280     this.animating = false;
52281     this.wasLeaf = true;
52282     this.ecc = 'x-tree-ec-icon x-tree-elbow';
52283     this.emptyIcon = Ext.BLANK_IMAGE_URL;
52284 };
52285
52286 Ext.tree.TreeNodeUI.prototype = {
52287     // private
52288     removeChild : function(node){
52289         if(this.rendered){
52290             this.ctNode.removeChild(node.ui.getEl());
52291         }
52292     },
52293
52294     // private
52295     beforeLoad : function(){
52296          this.addClass("x-tree-node-loading");
52297     },
52298
52299     // private
52300     afterLoad : function(){
52301          this.removeClass("x-tree-node-loading");
52302     },
52303
52304     // private
52305     onTextChange : function(node, text, oldText){
52306         if(this.rendered){
52307             this.textNode.innerHTML = text;
52308         }
52309     },
52310
52311     // private
52312     onDisableChange : function(node, state){
52313         this.disabled = state;
52314         if (this.checkbox) {
52315             this.checkbox.disabled = state;
52316         }
52317         if(state){
52318             this.addClass("x-tree-node-disabled");
52319         }else{
52320             this.removeClass("x-tree-node-disabled");
52321         }
52322     },
52323
52324     // private
52325     onSelectedChange : function(state){
52326         if(state){
52327             this.focus();
52328             this.addClass("x-tree-selected");
52329         }else{
52330             //this.blur();
52331             this.removeClass("x-tree-selected");
52332         }
52333     },
52334
52335     // private
52336     onMove : function(tree, node, oldParent, newParent, index, refNode){
52337         this.childIndent = null;
52338         if(this.rendered){
52339             var targetNode = newParent.ui.getContainer();
52340             if(!targetNode){//target not rendered
52341                 this.holder = document.createElement("div");
52342                 this.holder.appendChild(this.wrap);
52343                 return;
52344             }
52345             var insertBefore = refNode ? refNode.ui.getEl() : null;
52346             if(insertBefore){
52347                 targetNode.insertBefore(this.wrap, insertBefore);
52348             }else{
52349                 targetNode.appendChild(this.wrap);
52350             }
52351             this.node.renderIndent(true, oldParent != newParent);
52352         }
52353     },
52354
52355 /**
52356  * Adds one or more CSS classes to the node's UI element.
52357  * Duplicate classes are automatically filtered out.
52358  * @param {String/Array} className The CSS class to add, or an array of classes
52359  */
52360     addClass : function(cls){
52361         if(this.elNode){
52362             Ext.fly(this.elNode).addClass(cls);
52363         }
52364     },
52365
52366 /**
52367  * Removes one or more CSS classes from the node's UI element.
52368  * @param {String/Array} className The CSS class to remove, or an array of classes
52369  */
52370     removeClass : function(cls){
52371         if(this.elNode){
52372             Ext.fly(this.elNode).removeClass(cls);
52373         }
52374     },
52375
52376     // private
52377     remove : function(){
52378         if(this.rendered){
52379             this.holder = document.createElement("div");
52380             this.holder.appendChild(this.wrap);
52381         }
52382     },
52383
52384     // private
52385     fireEvent : function(){
52386         return this.node.fireEvent.apply(this.node, arguments);
52387     },
52388
52389     // private
52390     initEvents : function(){
52391         this.node.on("move", this.onMove, this);
52392
52393         if(this.node.disabled){
52394             this.onDisableChange(this.node, true);
52395         }
52396         if(this.node.hidden){
52397             this.hide();
52398         }
52399         var ot = this.node.getOwnerTree();
52400         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
52401         if(dd && (!this.node.isRoot || ot.rootVisible)){
52402             Ext.dd.Registry.register(this.elNode, {
52403                 node: this.node,
52404                 handles: this.getDDHandles(),
52405                 isHandle: false
52406             });
52407         }
52408     },
52409
52410     // private
52411     getDDHandles : function(){
52412         return [this.iconNode, this.textNode, this.elNode];
52413     },
52414
52415 /**
52416  * Hides this node.
52417  */
52418     hide : function(){
52419         this.node.hidden = true;
52420         if(this.wrap){
52421             this.wrap.style.display = "none";
52422         }
52423     },
52424
52425 /**
52426  * Shows this node.
52427  */
52428     show : function(){
52429         this.node.hidden = false;
52430         if(this.wrap){
52431             this.wrap.style.display = "";
52432         }
52433     },
52434
52435     // private
52436     onContextMenu : function(e){
52437         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
52438             e.preventDefault();
52439             this.focus();
52440             this.fireEvent("contextmenu", this.node, e);
52441         }
52442     },
52443
52444     // private
52445     onClick : function(e){
52446         if(this.dropping){
52447             e.stopEvent();
52448             return;
52449         }
52450         if(this.fireEvent("beforeclick", this.node, e) !== false){
52451             var a = e.getTarget('a');
52452             if(!this.disabled && this.node.attributes.href && a){
52453                 this.fireEvent("click", this.node, e);
52454                 return;
52455             }else if(a && e.ctrlKey){
52456                 e.stopEvent();
52457             }
52458             e.preventDefault();
52459             if(this.disabled){
52460                 return;
52461             }
52462
52463             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
52464                 this.node.toggle();
52465             }
52466
52467             this.fireEvent("click", this.node, e);
52468         }else{
52469             e.stopEvent();
52470         }
52471     },
52472
52473     // private
52474     onDblClick : function(e){
52475         e.preventDefault();
52476         if(this.disabled){
52477             return;
52478         }
52479         if(this.fireEvent("beforedblclick", this.node, e) !== false){
52480             if(this.checkbox){
52481                 this.toggleCheck();
52482             }
52483             if(!this.animating && this.node.isExpandable()){
52484                 this.node.toggle();
52485             }
52486             this.fireEvent("dblclick", this.node, e);
52487         }
52488     },
52489
52490     onOver : function(e){
52491         this.addClass('x-tree-node-over');
52492     },
52493
52494     onOut : function(e){
52495         this.removeClass('x-tree-node-over');
52496     },
52497
52498     // private
52499     onCheckChange : function(){
52500         var checked = this.checkbox.checked;
52501         // fix for IE6
52502         this.checkbox.defaultChecked = checked;
52503         this.node.attributes.checked = checked;
52504         this.fireEvent('checkchange', this.node, checked);
52505     },
52506
52507     // private
52508     ecClick : function(e){
52509         if(!this.animating && this.node.isExpandable()){
52510             this.node.toggle();
52511         }
52512     },
52513
52514     // private
52515     startDrop : function(){
52516         this.dropping = true;
52517     },
52518
52519     // delayed drop so the click event doesn't get fired on a drop
52520     endDrop : function(){
52521        setTimeout(function(){
52522            this.dropping = false;
52523        }.createDelegate(this), 50);
52524     },
52525
52526     // private
52527     expand : function(){
52528         this.updateExpandIcon();
52529         this.ctNode.style.display = "";
52530     },
52531
52532     // private
52533     focus : function(){
52534         if(!this.node.preventHScroll){
52535             try{this.anchor.focus();
52536             }catch(e){}
52537         }else{
52538             try{
52539                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
52540                 var l = noscroll.scrollLeft;
52541                 this.anchor.focus();
52542                 noscroll.scrollLeft = l;
52543             }catch(e){}
52544         }
52545     },
52546
52547 /**
52548  * Sets the checked status of the tree node to the passed value, or, if no value was passed,
52549  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.
52550  * @param {Boolean} value (optional) The new checked status.
52551  */
52552     toggleCheck : function(value){
52553         var cb = this.checkbox;
52554         if(cb){
52555             cb.checked = (value === undefined ? !cb.checked : value);
52556             this.onCheckChange();
52557         }
52558     },
52559
52560     // private
52561     blur : function(){
52562         try{
52563             this.anchor.blur();
52564         }catch(e){}
52565     },
52566
52567     // private
52568     animExpand : function(callback){
52569         var ct = Ext.get(this.ctNode);
52570         ct.stopFx();
52571         if(!this.node.isExpandable()){
52572             this.updateExpandIcon();
52573             this.ctNode.style.display = "";
52574             Ext.callback(callback);
52575             return;
52576         }
52577         this.animating = true;
52578         this.updateExpandIcon();
52579
52580         ct.slideIn('t', {
52581            callback : function(){
52582                this.animating = false;
52583                Ext.callback(callback);
52584             },
52585             scope: this,
52586             duration: this.node.ownerTree.duration || .25
52587         });
52588     },
52589
52590     // private
52591     highlight : function(){
52592         var tree = this.node.getOwnerTree();
52593         Ext.fly(this.wrap).highlight(
52594             tree.hlColor || "C3DAF9",
52595             {endColor: tree.hlBaseColor}
52596         );
52597     },
52598
52599     // private
52600     collapse : function(){
52601         this.updateExpandIcon();
52602         this.ctNode.style.display = "none";
52603     },
52604
52605     // private
52606     animCollapse : function(callback){
52607         var ct = Ext.get(this.ctNode);
52608         ct.enableDisplayMode('block');
52609         ct.stopFx();
52610
52611         this.animating = true;
52612         this.updateExpandIcon();
52613
52614         ct.slideOut('t', {
52615             callback : function(){
52616                this.animating = false;
52617                Ext.callback(callback);
52618             },
52619             scope: this,
52620             duration: this.node.ownerTree.duration || .25
52621         });
52622     },
52623
52624     // private
52625     getContainer : function(){
52626         return this.ctNode;
52627     },
52628
52629 /**
52630  * Returns the element which encapsulates this node.
52631  * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.
52632  */
52633     getEl : function(){
52634         return this.wrap;
52635     },
52636
52637     // private
52638     appendDDGhost : function(ghostNode){
52639         ghostNode.appendChild(this.elNode.cloneNode(true));
52640     },
52641
52642     // private
52643     getDDRepairXY : function(){
52644         return Ext.lib.Dom.getXY(this.iconNode);
52645     },
52646
52647     // private
52648     onRender : function(){
52649         this.render();
52650     },
52651
52652     // private
52653     render : function(bulkRender){
52654         var n = this.node, a = n.attributes;
52655         var targetNode = n.parentNode ?
52656               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
52657
52658         if(!this.rendered){
52659             this.rendered = true;
52660
52661             this.renderElements(n, a, targetNode, bulkRender);
52662
52663             if(a.qtip){
52664                if(this.textNode.setAttributeNS){
52665                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
52666                    if(a.qtipTitle){
52667                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
52668                    }
52669                }else{
52670                    this.textNode.setAttribute("ext:qtip", a.qtip);
52671                    if(a.qtipTitle){
52672                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
52673                    }
52674                }
52675             }else if(a.qtipCfg){
52676                 a.qtipCfg.target = Ext.id(this.textNode);
52677                 Ext.QuickTips.register(a.qtipCfg);
52678             }
52679             this.initEvents();
52680             if(!this.node.expanded){
52681                 this.updateExpandIcon(true);
52682             }
52683         }else{
52684             if(bulkRender === true) {
52685                 targetNode.appendChild(this.wrap);
52686             }
52687         }
52688     },
52689
52690     // private
52691     renderElements : function(n, a, targetNode, bulkRender){
52692         // add some indent caching, this helps performance when rendering a large tree
52693         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
52694
52695         var cb = Ext.isBoolean(a.checked),
52696             nel,
52697             href = a.href ? a.href : Ext.isGecko ? "" : "#",
52698             buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',
52699             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
52700             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
52701             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
52702             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
52703             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
52704              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
52705             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
52706             "</li>"].join('');
52707
52708         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
52709             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
52710         }else{
52711             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
52712         }
52713
52714         this.elNode = this.wrap.childNodes[0];
52715         this.ctNode = this.wrap.childNodes[1];
52716         var cs = this.elNode.childNodes;
52717         this.indentNode = cs[0];
52718         this.ecNode = cs[1];
52719         this.iconNode = cs[2];
52720         var index = 3;
52721         if(cb){
52722             this.checkbox = cs[3];
52723             // fix for IE6
52724             this.checkbox.defaultChecked = this.checkbox.checked;
52725             index++;
52726         }
52727         this.anchor = cs[index];
52728         this.textNode = cs[index].firstChild;
52729     },
52730
52731 /**
52732  * Returns the &lt;a> element that provides focus for the node's UI.
52733  * @return {HtmlElement} The DOM anchor element.
52734  */
52735     getAnchor : function(){
52736         return this.anchor;
52737     },
52738
52739 /**
52740  * Returns the text node.
52741  * @return {HtmlNode} The DOM text node.
52742  */
52743     getTextEl : function(){
52744         return this.textNode;
52745     },
52746
52747 /**
52748  * Returns the icon &lt;img> element.
52749  * @return {HtmlElement} The DOM image element.
52750  */
52751     getIconEl : function(){
52752         return this.iconNode;
52753     },
52754
52755 /**
52756  * Returns the checked status of the node. If the node was rendered with no
52757  * checkbox, it returns false.
52758  * @return {Boolean} The checked flag.
52759  */
52760     isChecked : function(){
52761         return this.checkbox ? this.checkbox.checked : false;
52762     },
52763
52764     // private
52765     updateExpandIcon : function(){
52766         if(this.rendered){
52767             var n = this.node,
52768                 c1,
52769                 c2,
52770                 cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",
52771                 hasChild = n.hasChildNodes();
52772             if(hasChild || n.attributes.expandable){
52773                 if(n.expanded){
52774                     cls += "-minus";
52775                     c1 = "x-tree-node-collapsed";
52776                     c2 = "x-tree-node-expanded";
52777                 }else{
52778                     cls += "-plus";
52779                     c1 = "x-tree-node-expanded";
52780                     c2 = "x-tree-node-collapsed";
52781                 }
52782                 if(this.wasLeaf){
52783                     this.removeClass("x-tree-node-leaf");
52784                     this.wasLeaf = false;
52785                 }
52786                 if(this.c1 != c1 || this.c2 != c2){
52787                     Ext.fly(this.elNode).replaceClass(c1, c2);
52788                     this.c1 = c1; this.c2 = c2;
52789                 }
52790             }else{
52791                 if(!this.wasLeaf){
52792                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-collapsed");
52793                     delete this.c1;
52794                     delete this.c2;
52795                     this.wasLeaf = true;
52796                 }
52797             }
52798             var ecc = "x-tree-ec-icon "+cls;
52799             if(this.ecc != ecc){
52800                 this.ecNode.className = ecc;
52801                 this.ecc = ecc;
52802             }
52803         }
52804     },
52805
52806     // private
52807     onIdChange: function(id){
52808         if(this.rendered){
52809             this.elNode.setAttribute('ext:tree-node-id', id);
52810         }
52811     },
52812
52813     // private
52814     getChildIndent : function(){
52815         if(!this.childIndent){
52816             var buf = [],
52817                 p = this.node;
52818             while(p){
52819                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
52820                     if(!p.isLast()) {
52821                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
52822                     } else {
52823                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
52824                     }
52825                 }
52826                 p = p.parentNode;
52827             }
52828             this.childIndent = buf.join("");
52829         }
52830         return this.childIndent;
52831     },
52832
52833     // private
52834     renderIndent : function(){
52835         if(this.rendered){
52836             var indent = "",
52837                 p = this.node.parentNode;
52838             if(p){
52839                 indent = p.ui.getChildIndent();
52840             }
52841             if(this.indentMarkup != indent){ // don't rerender if not required
52842                 this.indentNode.innerHTML = indent;
52843                 this.indentMarkup = indent;
52844             }
52845             this.updateExpandIcon();
52846         }
52847     },
52848
52849     destroy : function(){
52850         if(this.elNode){
52851             Ext.dd.Registry.unregister(this.elNode.id);
52852         }
52853
52854         Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){
52855             if(this[el]){
52856                 Ext.fly(this[el]).remove();
52857                 delete this[el];
52858             }
52859         }, this);
52860         delete this.node;
52861     }
52862 };
52863
52864 /**
52865  * @class Ext.tree.RootTreeNodeUI
52866  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
52867  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
52868  * <p>
52869  * If you are customizing the Tree's user interface, you
52870  * may need to extend this class, but you should never need to instantiate this class.<br>
52871  */
52872 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
52873     // private
52874     render : function(){
52875         if(!this.rendered){
52876             var targetNode = this.node.ownerTree.innerCt.dom;
52877             this.node.expanded = true;
52878             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
52879             this.wrap = this.ctNode = targetNode.firstChild;
52880         }
52881     },
52882     collapse : Ext.emptyFn,
52883     expand : Ext.emptyFn
52884 });/**
52885  * @class Ext.tree.TreeLoader
52886  * @extends Ext.util.Observable
52887  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child
52888  * nodes from a specified URL. The response must be a JavaScript Array definition
52889  * whose elements are node definition objects. e.g.:
52890  * <pre><code>
52891     [{
52892         id: 1,
52893         text: 'A leaf Node',
52894         leaf: true
52895     },{
52896         id: 2,
52897         text: 'A folder Node',
52898         children: [{
52899             id: 3,
52900             text: 'A child Node',
52901             leaf: true
52902         }]
52903    }]
52904 </code></pre>
52905  * <br><br>
52906  * A server request is sent, and child nodes are loaded only when a node is expanded.
52907  * The loading node's id is passed to the server under the parameter name "node" to
52908  * enable the server to produce the correct child nodes.
52909  * <br><br>
52910  * To pass extra parameters, an event handler may be attached to the "beforeload"
52911  * event, and the parameters specified in the TreeLoader's baseParams property:
52912  * <pre><code>
52913     myTreeLoader.on("beforeload", function(treeLoader, node) {
52914         this.baseParams.category = node.attributes.category;
52915     }, this);
52916 </code></pre>
52917  * This would pass an HTTP parameter called "category" to the server containing
52918  * the value of the Node's "category" attribute.
52919  * @constructor
52920  * Creates a new Treeloader.
52921  * @param {Object} config A config object containing config properties.
52922  */
52923 Ext.tree.TreeLoader = function(config){
52924     this.baseParams = {};
52925     Ext.apply(this, config);
52926
52927     this.addEvents(
52928         /**
52929          * @event beforeload
52930          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
52931          * @param {Object} This TreeLoader object.
52932          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
52933          * @param {Object} callback The callback function specified in the {@link #load} call.
52934          */
52935         "beforeload",
52936         /**
52937          * @event load
52938          * Fires when the node has been successfuly loaded.
52939          * @param {Object} This TreeLoader object.
52940          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
52941          * @param {Object} response The response object containing the data from the server.
52942          */
52943         "load",
52944         /**
52945          * @event loadexception
52946          * Fires if the network request failed.
52947          * @param {Object} This TreeLoader object.
52948          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
52949          * @param {Object} response The response object containing the data from the server.
52950          */
52951         "loadexception"
52952     );
52953     Ext.tree.TreeLoader.superclass.constructor.call(this);
52954     if(Ext.isString(this.paramOrder)){
52955         this.paramOrder = this.paramOrder.split(/[\s,|]/);
52956     }
52957 };
52958
52959 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {
52960     /**
52961     * @cfg {String} dataUrl The URL from which to request a Json string which
52962     * specifies an array of node definition objects representing the child nodes
52963     * to be loaded.
52964     */
52965     /**
52966      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).
52967      */
52968     /**
52969      * @cfg {String} url Equivalent to {@link #dataUrl}.
52970      */
52971     /**
52972      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.
52973      */
52974     /**
52975     * @cfg {Object} baseParams (optional) An object containing properties which
52976     * specify HTTP parameters to be passed to each request for child nodes.
52977     */
52978     /**
52979     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
52980     * created by this loader. If the attributes sent by the server have an attribute in this object,
52981     * they take priority.
52982     */
52983     /**
52984     * @cfg {Object} uiProviders (optional) An object containing properties which
52985     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional
52986     * <i>uiProvider</i> attribute of a returned child node is a string rather
52987     * than a reference to a TreeNodeUI implementation, then that string value
52988     * is used as a property name in the uiProviders object.
52989     */
52990     uiProviders : {},
52991
52992     /**
52993     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
52994     * child nodes before loading.
52995     */
52996     clearOnLoad : true,
52997
52998     /**
52999      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. Only used when using directFn.
53000      * Specifies the params in the order in which they must be passed to the server-side Direct method
53001      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
53002      * comma, or pipe. For example,
53003      * any of the following would be acceptable:<pre><code>
53004 nodeParameter: 'node',
53005 paramOrder: ['param1','param2','param3']
53006 paramOrder: 'node param1 param2 param3'
53007 paramOrder: 'param1,node,param2,param3'
53008 paramOrder: 'param1|param2|param|node'
53009      </code></pre>
53010      */
53011     paramOrder: undefined,
53012
53013     /**
53014      * @cfg {Boolean} paramsAsHash Only used when using directFn.
53015      * Send parameters as a collection of named arguments (defaults to <tt>false</tt>). Providing a
53016      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
53017      */
53018     paramsAsHash: false,
53019
53020     /**
53021      * @cfg {String} nodeParameter The name of the parameter sent to the server which contains
53022      * the identifier of the node. Defaults to <tt>'node'</tt>.
53023      */
53024     nodeParameter: 'node',
53025
53026     /**
53027      * @cfg {Function} directFn
53028      * Function to call when executing a request.
53029      */
53030     directFn : undefined,
53031
53032     /**
53033      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.
53034      * This is called automatically when a node is expanded, but may be used to reload
53035      * a node (or append new children if the {@link #clearOnLoad} option is false.)
53036      * @param {Ext.tree.TreeNode} node
53037      * @param {Function} callback Function to call after the node has been loaded. The
53038      * function is passed the TreeNode which was requested to be loaded.
53039      * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed.
53040      * defaults to the loaded TreeNode.
53041      */
53042     load : function(node, callback, scope){
53043         if(this.clearOnLoad){
53044             while(node.firstChild){
53045                 node.removeChild(node.firstChild);
53046             }
53047         }
53048         if(this.doPreload(node)){ // preloaded json children
53049             this.runCallback(callback, scope || node, [node]);
53050         }else if(this.directFn || this.dataUrl || this.url){
53051             this.requestData(node, callback, scope || node);
53052         }
53053     },
53054
53055     doPreload : function(node){
53056         if(node.attributes.children){
53057             if(node.childNodes.length < 1){ // preloaded?
53058                 var cs = node.attributes.children;
53059                 node.beginUpdate();
53060                 for(var i = 0, len = cs.length; i < len; i++){
53061                     var cn = node.appendChild(this.createNode(cs[i]));
53062                     if(this.preloadChildren){
53063                         this.doPreload(cn);
53064                     }
53065                 }
53066                 node.endUpdate();
53067             }
53068             return true;
53069         }
53070         return false;
53071     },
53072
53073     getParams: function(node){
53074         var bp = Ext.apply({}, this.baseParams),
53075             np = this.nodeParameter,
53076             po = this.paramOrder;
53077
53078         np && (bp[ np ] = node.id);
53079
53080         if(this.directFn){
53081             var buf = [node.id];
53082             if(po){
53083                 // reset 'buf' if the nodeParameter was included in paramOrder
53084                 if(np && po.indexOf(np) > -1){
53085                     buf = [];
53086                 }
53087
53088                 for(var i = 0, len = po.length; i < len; i++){
53089                     buf.push(bp[ po[i] ]);
53090                 }
53091             }else if(this.paramsAsHash){
53092                 buf = [bp];
53093             }
53094             return buf;
53095         }else{
53096             return bp;
53097         }
53098     },
53099
53100     requestData : function(node, callback, scope){
53101         if(this.fireEvent("beforeload", this, node, callback) !== false){
53102             if(this.directFn){
53103                 var args = this.getParams(node);
53104                 args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true));
53105                 this.directFn.apply(window, args);
53106             }else{
53107                 this.transId = Ext.Ajax.request({
53108                     method:this.requestMethod,
53109                     url: this.dataUrl||this.url,
53110                     success: this.handleResponse,
53111                     failure: this.handleFailure,
53112                     scope: this,
53113                     argument: {callback: callback, node: node, scope: scope},
53114                     params: this.getParams(node)
53115                 });
53116             }
53117         }else{
53118             // if the load is cancelled, make sure we notify
53119             // the node that we are done
53120             this.runCallback(callback, scope || node, []);
53121         }
53122     },
53123
53124     processDirectResponse: function(result, response, args){
53125         if(response.status){
53126             this.handleResponse({
53127                 responseData: Ext.isArray(result) ? result : null,
53128                 responseText: result,
53129                 argument: args
53130             });
53131         }else{
53132             this.handleFailure({
53133                 argument: args
53134             });
53135         }
53136     },
53137
53138     // private
53139     runCallback: function(cb, scope, args){
53140         if(Ext.isFunction(cb)){
53141             cb.apply(scope, args);
53142         }
53143     },
53144
53145     isLoading : function(){
53146         return !!this.transId;
53147     },
53148
53149     abort : function(){
53150         if(this.isLoading()){
53151             Ext.Ajax.abort(this.transId);
53152         }
53153     },
53154
53155     /**
53156     * <p>Override this function for custom TreeNode node implementation, or to
53157     * modify the attributes at creation time.</p>
53158     * Example:<pre><code>
53159 new Ext.tree.TreePanel({
53160     ...
53161     loader: new Ext.tree.TreeLoader({
53162         url: 'dataUrl',
53163         createNode: function(attr) {
53164 //          Allow consolidation consignments to have
53165 //          consignments dropped into them.
53166             if (attr.isConsolidation) {
53167                 attr.iconCls = 'x-consol',
53168                 attr.allowDrop = true;
53169             }
53170             return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
53171         }
53172     }),
53173     ...
53174 });
53175 </code></pre>
53176     * @param attr {Object} The attributes from which to create the new node.
53177     */
53178     createNode : function(attr){
53179         // apply baseAttrs, nice idea Corey!
53180         if(this.baseAttrs){
53181             Ext.applyIf(attr, this.baseAttrs);
53182         }
53183         if(this.applyLoader !== false && !attr.loader){
53184             attr.loader = this;
53185         }
53186         if(Ext.isString(attr.uiProvider)){
53187            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);
53188         }
53189         if(attr.nodeType){
53190             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);
53191         }else{
53192             return attr.leaf ?
53193                         new Ext.tree.TreeNode(attr) :
53194                         new Ext.tree.AsyncTreeNode(attr);
53195         }
53196     },
53197
53198     processResponse : function(response, node, callback, scope){
53199         var json = response.responseText;
53200         try {
53201             var o = response.responseData || Ext.decode(json);
53202             node.beginUpdate();
53203             for(var i = 0, len = o.length; i < len; i++){
53204                 var n = this.createNode(o[i]);
53205                 if(n){
53206                     node.appendChild(n);
53207                 }
53208             }
53209             node.endUpdate();
53210             this.runCallback(callback, scope || node, [node]);
53211         }catch(e){
53212             this.handleFailure(response);
53213         }
53214     },
53215
53216     handleResponse : function(response){
53217         this.transId = false;
53218         var a = response.argument;
53219         this.processResponse(response, a.node, a.callback, a.scope);
53220         this.fireEvent("load", this, a.node, response);
53221     },
53222
53223     handleFailure : function(response){
53224         this.transId = false;
53225         var a = response.argument;
53226         this.fireEvent("loadexception", this, a.node, response);
53227         this.runCallback(a.callback, a.scope || a.node, [a.node]);
53228     },
53229
53230     destroy : function(){
53231         this.abort();
53232         this.purgeListeners();
53233     }
53234 });/**
53235  * @class Ext.tree.TreeFilter
53236  * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
53237  * @param {TreePanel} tree
53238  * @param {Object} config (optional)
53239  */
53240 Ext.tree.TreeFilter = function(tree, config){
53241     this.tree = tree;
53242     this.filtered = {};
53243     Ext.apply(this, config);
53244 };
53245
53246 Ext.tree.TreeFilter.prototype = {
53247     clearBlank:false,
53248     reverse:false,
53249     autoClear:false,
53250     remove:false,
53251
53252      /**
53253      * Filter the data by a specific attribute.
53254      * @param {String/RegExp} value Either string that the attribute value
53255      * should start with or a RegExp to test against the attribute
53256      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
53257      * @param {TreeNode} startNode (optional) The node to start the filter at.
53258      */
53259     filter : function(value, attr, startNode){
53260         attr = attr || "text";
53261         var f;
53262         if(typeof value == "string"){
53263             var vlen = value.length;
53264             // auto clear empty filter
53265             if(vlen == 0 && this.clearBlank){
53266                 this.clear();
53267                 return;
53268             }
53269             value = value.toLowerCase();
53270             f = function(n){
53271                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
53272             };
53273         }else if(value.exec){ // regex?
53274             f = function(n){
53275                 return value.test(n.attributes[attr]);
53276             };
53277         }else{
53278             throw 'Illegal filter type, must be string or regex';
53279         }
53280         this.filterBy(f, null, startNode);
53281         },
53282
53283     /**
53284      * Filter by a function. The passed function will be called with each
53285      * node in the tree (or from the startNode). If the function returns true, the node is kept
53286      * otherwise it is filtered. If a node is filtered, its children are also filtered.
53287      * @param {Function} fn The filter function
53288      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
53289      */
53290     filterBy : function(fn, scope, startNode){
53291         startNode = startNode || this.tree.root;
53292         if(this.autoClear){
53293             this.clear();
53294         }
53295         var af = this.filtered, rv = this.reverse;
53296         var f = function(n){
53297             if(n == startNode){
53298                 return true;
53299             }
53300             if(af[n.id]){
53301                 return false;
53302             }
53303             var m = fn.call(scope || n, n);
53304             if(!m || rv){
53305                 af[n.id] = n;
53306                 n.ui.hide();
53307                 return false;
53308             }
53309             return true;
53310         };
53311         startNode.cascade(f);
53312         if(this.remove){
53313            for(var id in af){
53314                if(typeof id != "function"){
53315                    var n = af[id];
53316                    if(n && n.parentNode){
53317                        n.parentNode.removeChild(n);
53318                    }
53319                }
53320            }
53321         }
53322     },
53323
53324     /**
53325      * Clears the current filter. Note: with the "remove" option
53326      * set a filter cannot be cleared.
53327      */
53328     clear : function(){
53329         var t = this.tree;
53330         var af = this.filtered;
53331         for(var id in af){
53332             if(typeof id != "function"){
53333                 var n = af[id];
53334                 if(n){
53335                     n.ui.show();
53336                 }
53337             }
53338         }
53339         this.filtered = {};
53340     }
53341 };
53342 /**
53343  * @class Ext.tree.TreeSorter
53344  * Provides sorting of nodes in a {@link Ext.tree.TreePanel}.  The TreeSorter automatically monitors events on the
53345  * associated TreePanel that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).
53346  * Example usage:<br />
53347  * <pre><code>
53348 new Ext.tree.TreeSorter(myTree, {
53349     folderSort: true,
53350     dir: "desc",
53351     sortType: function(node) {
53352         // sort by a custom, typed attribute:
53353         return parseInt(node.id, 10);
53354     }
53355 });
53356 </code></pre>
53357  * @constructor
53358  * @param {TreePanel} tree
53359  * @param {Object} config
53360  */
53361 Ext.tree.TreeSorter = function(tree, config){
53362     /**
53363      * @cfg {Boolean} folderSort True to sort leaf nodes under non-leaf nodes (defaults to false)
53364      */
53365     /**
53366      * @cfg {String} property The named attribute on the node to sort by (defaults to "text").  Note that this
53367      * property is only used if no {@link #sortType} function is specified, otherwise it is ignored.
53368      */
53369     /**
53370      * @cfg {String} dir The direction to sort ("asc" or "desc," case-insensitive, defaults to "asc")
53371      */
53372     /**
53373      * @cfg {String} leafAttr The attribute used to determine leaf nodes when {@link #folderSort} = true (defaults to "leaf")
53374      */
53375     /**
53376      * @cfg {Boolean} caseSensitive true for case-sensitive sort (defaults to false)
53377      */
53378     /**
53379      * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting.  The function
53380      * will be called with a single parameter (the {@link Ext.tree.TreeNode} being evaluated) and is expected to return
53381      * the node's sort value cast to the specific data type required for sorting.  This could be used, for example, when
53382      * a node's text (or other attribute) should be sorted as a date or numeric value.  See the class description for
53383      * example usage.  Note that if a sortType is specified, any {@link #property} config will be ignored.
53384      */
53385
53386     Ext.apply(this, config);
53387     tree.on("beforechildrenrendered", this.doSort, this);
53388     tree.on("append", this.updateSort, this);
53389     tree.on("insert", this.updateSort, this);
53390     tree.on("textchange", this.updateSortParent, this);
53391
53392     var dsc = this.dir && this.dir.toLowerCase() == "desc";
53393     var p = this.property || "text";
53394     var sortType = this.sortType;
53395     var fs = this.folderSort;
53396     var cs = this.caseSensitive === true;
53397     var leafAttr = this.leafAttr || 'leaf';
53398
53399     this.sortFn = function(n1, n2){
53400         if(fs){
53401             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
53402                 return 1;
53403             }
53404             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
53405                 return -1;
53406             }
53407         }
53408         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
53409         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
53410         if(v1 < v2){
53411             return dsc ? +1 : -1;
53412         }else if(v1 > v2){
53413             return dsc ? -1 : +1;
53414         }else{
53415             return 0;
53416         }
53417     };
53418 };
53419
53420 Ext.tree.TreeSorter.prototype = {
53421     doSort : function(node){
53422         node.sort(this.sortFn);
53423     },
53424
53425     compareNodes : function(n1, n2){
53426         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
53427     },
53428
53429     updateSort : function(tree, node){
53430         if(node.childrenRendered){
53431             this.doSort.defer(1, this, [node]);
53432         }
53433     },
53434
53435     updateSortParent : function(node){
53436         var p = node.parentNode;
53437         if(p && p.childrenRendered){
53438             this.doSort.defer(1, this, [p]);
53439         }
53440     }
53441 };/**
53442  * @class Ext.tree.TreeDropZone
53443  * @extends Ext.dd.DropZone
53444  * @constructor
53445  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping
53446  * @param {Object} config
53447  */
53448 if(Ext.dd.DropZone){
53449     
53450 Ext.tree.TreeDropZone = function(tree, config){
53451     /**
53452      * @cfg {Boolean} allowParentInsert
53453      * Allow inserting a dragged node between an expanded parent node and its first child that will become a
53454      * sibling of the parent when dropped (defaults to false)
53455      */
53456     this.allowParentInsert = config.allowParentInsert || false;
53457     /**
53458      * @cfg {String} allowContainerDrop
53459      * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)
53460      */
53461     this.allowContainerDrop = config.allowContainerDrop || false;
53462     /**
53463      * @cfg {String} appendOnly
53464      * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)
53465      */
53466     this.appendOnly = config.appendOnly || false;
53467
53468     Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);
53469     /**
53470     * The TreePanel for this drop zone
53471     * @type Ext.tree.TreePanel
53472     * @property
53473     */
53474     this.tree = tree;
53475     /**
53476     * Arbitrary data that can be associated with this tree and will be included in the event object that gets
53477     * passed to any nodedragover event handler (defaults to {})
53478     * @type Ext.tree.TreePanel
53479     * @property
53480     */
53481     this.dragOverData = {};
53482     // private
53483     this.lastInsertClass = "x-tree-no-status";
53484 };
53485
53486 Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {
53487     /**
53488      * @cfg {String} ddGroup
53489      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
53490      * interact with other drag drop objects in the same group (defaults to 'TreeDD').
53491      */
53492     ddGroup : "TreeDD",
53493
53494     /**
53495      * @cfg {String} expandDelay
53496      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node
53497      * over the target (defaults to 1000)
53498      */
53499     expandDelay : 1000,
53500
53501     // private
53502     expandNode : function(node){
53503         if(node.hasChildNodes() && !node.isExpanded()){
53504             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
53505         }
53506     },
53507
53508     // private
53509     queueExpand : function(node){
53510         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
53511     },
53512
53513     // private
53514     cancelExpand : function(){
53515         if(this.expandProcId){
53516             clearTimeout(this.expandProcId);
53517             this.expandProcId = false;
53518         }
53519     },
53520
53521     // private
53522     isValidDropPoint : function(n, pt, dd, e, data){
53523         if(!n || !data){ return false; }
53524         var targetNode = n.node;
53525         var dropNode = data.node;
53526         // default drop rules
53527         if(!(targetNode && targetNode.isTarget && pt)){
53528             return false;
53529         }
53530         if(pt == "append" && targetNode.allowChildren === false){
53531             return false;
53532         }
53533         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
53534             return false;
53535         }
53536         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
53537             return false;
53538         }
53539         // reuse the object
53540         var overEvent = this.dragOverData;
53541         overEvent.tree = this.tree;
53542         overEvent.target = targetNode;
53543         overEvent.data = data;
53544         overEvent.point = pt;
53545         overEvent.source = dd;
53546         overEvent.rawEvent = e;
53547         overEvent.dropNode = dropNode;
53548         overEvent.cancel = false;  
53549         var result = this.tree.fireEvent("nodedragover", overEvent);
53550         return overEvent.cancel === false && result !== false;
53551     },
53552
53553     // private
53554     getDropPoint : function(e, n, dd){
53555         var tn = n.node;
53556         if(tn.isRoot){
53557             return tn.allowChildren !== false ? "append" : false; // always append for root
53558         }
53559         var dragEl = n.ddel;
53560         var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
53561         var y = Ext.lib.Event.getPageY(e);
53562         var noAppend = tn.allowChildren === false || tn.isLeaf();
53563         if(this.appendOnly || tn.parentNode.allowChildren === false){
53564             return noAppend ? false : "append";
53565         }
53566         var noBelow = false;
53567         if(!this.allowParentInsert){
53568             noBelow = tn.hasChildNodes() && tn.isExpanded();
53569         }
53570         var q = (b - t) / (noAppend ? 2 : 3);
53571         if(y >= t && y < (t + q)){
53572             return "above";
53573         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
53574             return "below";
53575         }else{
53576             return "append";
53577         }
53578     },
53579
53580     // private
53581     onNodeEnter : function(n, dd, e, data){
53582         this.cancelExpand();
53583     },
53584     
53585     onContainerOver : function(dd, e, data) {
53586         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
53587             return this.dropAllowed;
53588         }
53589         return this.dropNotAllowed;
53590     },
53591
53592     // private
53593     onNodeOver : function(n, dd, e, data){
53594         var pt = this.getDropPoint(e, n, dd);
53595         var node = n.node;
53596         
53597         // auto node expand check
53598         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
53599             this.queueExpand(node);
53600         }else if(pt != "append"){
53601             this.cancelExpand();
53602         }
53603         
53604         // set the insert point style on the target node
53605         var returnCls = this.dropNotAllowed;
53606         if(this.isValidDropPoint(n, pt, dd, e, data)){
53607            if(pt){
53608                var el = n.ddel;
53609                var cls;
53610                if(pt == "above"){
53611                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
53612                    cls = "x-tree-drag-insert-above";
53613                }else if(pt == "below"){
53614                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
53615                    cls = "x-tree-drag-insert-below";
53616                }else{
53617                    returnCls = "x-tree-drop-ok-append";
53618                    cls = "x-tree-drag-append";
53619                }
53620                if(this.lastInsertClass != cls){
53621                    Ext.fly(el).replaceClass(this.lastInsertClass, cls);
53622                    this.lastInsertClass = cls;
53623                }
53624            }
53625        }
53626        return returnCls;
53627     },
53628
53629     // private
53630     onNodeOut : function(n, dd, e, data){
53631         this.cancelExpand();
53632         this.removeDropIndicators(n);
53633     },
53634
53635     // private
53636     onNodeDrop : function(n, dd, e, data){
53637         var point = this.getDropPoint(e, n, dd);
53638         var targetNode = n.node;
53639         targetNode.ui.startDrop();
53640         if(!this.isValidDropPoint(n, point, dd, e, data)){
53641             targetNode.ui.endDrop();
53642             return false;
53643         }
53644         // first try to find the drop node
53645         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
53646         return this.processDrop(targetNode, data, point, dd, e, dropNode);
53647     },
53648     
53649     onContainerDrop : function(dd, e, data){
53650         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
53651             var targetNode = this.tree.getRootNode();       
53652             targetNode.ui.startDrop();
53653             var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null);
53654             return this.processDrop(targetNode, data, 'append', dd, e, dropNode);
53655         }
53656         return false;
53657     },
53658     
53659     // private
53660     processDrop: function(target, data, point, dd, e, dropNode){
53661         var dropEvent = {
53662             tree : this.tree,
53663             target: target,
53664             data: data,
53665             point: point,
53666             source: dd,
53667             rawEvent: e,
53668             dropNode: dropNode,
53669             cancel: !dropNode,
53670             dropStatus: false
53671         };
53672         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
53673         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
53674             target.ui.endDrop();
53675             return dropEvent.dropStatus;
53676         }
53677     
53678         target = dropEvent.target;
53679         if(point == 'append' && !target.isExpanded()){
53680             target.expand(false, null, function(){
53681                 this.completeDrop(dropEvent);
53682             }.createDelegate(this));
53683         }else{
53684             this.completeDrop(dropEvent);
53685         }
53686         return true;
53687     },
53688
53689     // private
53690     completeDrop : function(de){
53691         var ns = de.dropNode, p = de.point, t = de.target;
53692         if(!Ext.isArray(ns)){
53693             ns = [ns];
53694         }
53695         var n;
53696         for(var i = 0, len = ns.length; i < len; i++){
53697             n = ns[i];
53698             if(p == "above"){
53699                 t.parentNode.insertBefore(n, t);
53700             }else if(p == "below"){
53701                 t.parentNode.insertBefore(n, t.nextSibling);
53702             }else{
53703                 t.appendChild(n);
53704             }
53705         }
53706         n.ui.focus();
53707         if(Ext.enableFx && this.tree.hlDrop){
53708             n.ui.highlight();
53709         }
53710         t.ui.endDrop();
53711         this.tree.fireEvent("nodedrop", de);
53712     },
53713
53714     // private
53715     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
53716         if(Ext.enableFx && this.tree.hlDrop){
53717             dropNode.ui.focus();
53718             dropNode.ui.highlight();
53719         }
53720         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
53721     },
53722
53723     // private
53724     getTree : function(){
53725         return this.tree;
53726     },
53727
53728     // private
53729     removeDropIndicators : function(n){
53730         if(n && n.ddel){
53731             var el = n.ddel;
53732             Ext.fly(el).removeClass([
53733                     "x-tree-drag-insert-above",
53734                     "x-tree-drag-insert-below",
53735                     "x-tree-drag-append"]);
53736             this.lastInsertClass = "_noclass";
53737         }
53738     },
53739
53740     // private
53741     beforeDragDrop : function(target, e, id){
53742         this.cancelExpand();
53743         return true;
53744     },
53745
53746     // private
53747     afterRepair : function(data){
53748         if(data && Ext.enableFx){
53749             data.node.ui.highlight();
53750         }
53751         this.hideProxy();
53752     }    
53753 });
53754
53755 }/**
53756  * @class Ext.tree.TreeDragZone
53757  * @extends Ext.dd.DragZone
53758  * @constructor
53759  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dragging
53760  * @param {Object} config
53761  */
53762 if(Ext.dd.DragZone){
53763 Ext.tree.TreeDragZone = function(tree, config){
53764     Ext.tree.TreeDragZone.superclass.constructor.call(this, tree.innerCt, config);
53765     /**
53766     * The TreePanel for this drag zone
53767     * @type Ext.tree.TreePanel
53768     * @property
53769     */
53770     this.tree = tree;
53771 };
53772
53773 Ext.extend(Ext.tree.TreeDragZone, Ext.dd.DragZone, {
53774     /**
53775      * @cfg {String} ddGroup
53776      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
53777      * interact with other drag drop objects in the same group (defaults to 'TreeDD').
53778      */
53779     ddGroup : "TreeDD",
53780
53781     // private
53782     onBeforeDrag : function(data, e){
53783         var n = data.node;
53784         return n && n.draggable && !n.disabled;
53785     },
53786
53787     // private
53788     onInitDrag : function(e){
53789         var data = this.dragData;
53790         this.tree.getSelectionModel().select(data.node);
53791         this.tree.eventModel.disable();
53792         this.proxy.update("");
53793         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
53794         this.tree.fireEvent("startdrag", this.tree, data.node, e);
53795     },
53796
53797     // private
53798     getRepairXY : function(e, data){
53799         return data.node.ui.getDDRepairXY();
53800     },
53801
53802     // private
53803     onEndDrag : function(data, e){
53804         this.tree.eventModel.enable.defer(100, this.tree.eventModel);
53805         this.tree.fireEvent("enddrag", this.tree, data.node, e);
53806     },
53807
53808     // private
53809     onValidDrop : function(dd, e, id){
53810         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
53811         this.hideProxy();
53812     },
53813
53814     // private
53815     beforeInvalidDrop : function(e, id){
53816         // this scrolls the original position back into view
53817         var sm = this.tree.getSelectionModel();
53818         sm.clearSelections();
53819         sm.select(this.dragData.node);
53820     },
53821     
53822     // private
53823     afterRepair : function(){
53824         if (Ext.enableFx && this.tree.hlDrop) {
53825             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
53826         }
53827         this.dragging = false;
53828     }
53829 });
53830 }/**
53831  * @class Ext.tree.TreeEditor
53832  * @extends Ext.Editor
53833  * Provides editor functionality for inline tree node editing.  Any valid {@link Ext.form.Field} subclass can be used
53834  * as the editor field.
53835  * @constructor
53836  * @param {TreePanel} tree
53837  * @param {Object} fieldConfig (optional) Either a prebuilt {@link Ext.form.Field} instance or a Field config object
53838  * that will be applied to the default field instance (defaults to a {@link Ext.form.TextField}).
53839  * @param {Object} config (optional) A TreeEditor config object
53840  */
53841 Ext.tree.TreeEditor = function(tree, fc, config){
53842     fc = fc || {};
53843     var field = fc.events ? fc : new Ext.form.TextField(fc);
53844     
53845     Ext.tree.TreeEditor.superclass.constructor.call(this, field, config);
53846
53847     this.tree = tree;
53848
53849     if(!tree.rendered){
53850         tree.on('render', this.initEditor, this);
53851     }else{
53852         this.initEditor(tree);
53853     }
53854 };
53855
53856 Ext.extend(Ext.tree.TreeEditor, Ext.Editor, {
53857     /**
53858      * @cfg {String} alignment
53859      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "l-l").
53860      */
53861     alignment: "l-l",
53862     // inherit
53863     autoSize: false,
53864     /**
53865      * @cfg {Boolean} hideEl
53866      * True to hide the bound element while the editor is displayed (defaults to false)
53867      */
53868     hideEl : false,
53869     /**
53870      * @cfg {String} cls
53871      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
53872      */
53873     cls: "x-small-editor x-tree-editor",
53874     /**
53875      * @cfg {Boolean} shim
53876      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
53877      */
53878     shim:false,
53879     // inherit
53880     shadow:"frame",
53881     /**
53882      * @cfg {Number} maxWidth
53883      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
53884      * the containing tree element's size, it will be automatically limited for you to the container width, taking
53885      * scroll and client offsets into account prior to each edit.
53886      */
53887     maxWidth: 250,
53888     /**
53889      * @cfg {Number} editDelay The number of milliseconds between clicks to register a double-click that will trigger
53890      * editing on the current node (defaults to 350).  If two clicks occur on the same node within this time span,
53891      * the editor for the node will display, otherwise it will be processed as a regular click.
53892      */
53893     editDelay : 350,
53894
53895     initEditor : function(tree){
53896         tree.on({
53897             scope      : this,
53898             beforeclick: this.beforeNodeClick,
53899             dblclick   : this.onNodeDblClick
53900         });
53901         
53902         this.on({
53903             scope          : this,
53904             complete       : this.updateNode,
53905             beforestartedit: this.fitToTree,
53906             specialkey     : this.onSpecialKey
53907         });
53908         
53909         this.on('startedit', this.bindScroll, this, {delay:10});
53910     },
53911
53912     // private
53913     fitToTree : function(ed, el){
53914         var td = this.tree.getTreeEl().dom, nd = el.dom;
53915         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
53916             td.scrollLeft = nd.offsetLeft;
53917         }
53918         var w = Math.min(
53919                 this.maxWidth,
53920                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
53921         this.setSize(w, '');
53922     },
53923
53924     /**
53925      * Edit the text of the passed {@link Ext.tree.TreeNode TreeNode}.
53926      * @param node {Ext.tree.TreeNode} The TreeNode to edit. The TreeNode must be {@link Ext.tree.TreeNode#editable editable}.
53927      */
53928     triggerEdit : function(node, defer){
53929         this.completeEdit();
53930                 if(node.attributes.editable !== false){
53931            /**
53932             * The {@link Ext.tree.TreeNode TreeNode} this editor is bound to. Read-only.
53933             * @type Ext.tree.TreeNode
53934             * @property editNode
53935             */
53936                         this.editNode = node;
53937             if(this.tree.autoScroll){
53938                 Ext.fly(node.ui.getEl()).scrollIntoView(this.tree.body);
53939             }
53940             var value = node.text || '';
53941             if (!Ext.isGecko && Ext.isEmpty(node.text)){
53942                 node.setText('&#160;');
53943             }
53944             this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, value]);
53945             return false;
53946         }
53947     },
53948
53949     // private
53950     bindScroll : function(){
53951         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
53952     },
53953
53954     // private
53955     beforeNodeClick : function(node, e){
53956         clearTimeout(this.autoEditTimer);
53957         if(this.tree.getSelectionModel().isSelected(node)){
53958             e.stopEvent();
53959             return this.triggerEdit(node);
53960         }
53961     },
53962
53963     onNodeDblClick : function(node, e){
53964         clearTimeout(this.autoEditTimer);
53965     },
53966
53967     // private
53968     updateNode : function(ed, value){
53969         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
53970         this.editNode.setText(value);
53971     },
53972
53973     // private
53974     onHide : function(){
53975         Ext.tree.TreeEditor.superclass.onHide.call(this);
53976         if(this.editNode){
53977             this.editNode.ui.focus.defer(50, this.editNode.ui);
53978         }
53979     },
53980
53981     // private
53982     onSpecialKey : function(field, e){
53983         var k = e.getKey();
53984         if(k == e.ESC){
53985             e.stopEvent();
53986             this.cancelEdit();
53987         }else if(k == e.ENTER && !e.hasModifier()){
53988             e.stopEvent();
53989             this.completeEdit();
53990         }
53991     },
53992     
53993     onDestroy : function(){
53994         clearTimeout(this.autoEditTimer);
53995         Ext.tree.TreeEditor.superclass.onDestroy.call(this);
53996         var tree = this.tree;
53997         tree.un('beforeclick', this.beforeNodeClick, this);
53998         tree.un('dblclick', this.onNodeDblClick, this);
53999     }
54000 });/*! SWFObject v2.2 <http://code.google.com/p/swfobject/> 
54001     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
54002 */
54003
54004 var swfobject = function() {
54005     
54006     var UNDEF = "undefined",
54007         OBJECT = "object",
54008         SHOCKWAVE_FLASH = "Shockwave Flash",
54009         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
54010         FLASH_MIME_TYPE = "application/x-shockwave-flash",
54011         EXPRESS_INSTALL_ID = "SWFObjectExprInst",
54012         ON_READY_STATE_CHANGE = "onreadystatechange",
54013         
54014         win = window,
54015         doc = document,
54016         nav = navigator,
54017         
54018         plugin = false,
54019         domLoadFnArr = [main],
54020         regObjArr = [],
54021         objIdArr = [],
54022         listenersArr = [],
54023         storedAltContent,
54024         storedAltContentId,
54025         storedCallbackFn,
54026         storedCallbackObj,
54027         isDomLoaded = false,
54028         isExpressInstallActive = false,
54029         dynamicStylesheet,
54030         dynamicStylesheetMedia,
54031         autoHideShow = true,
54032     
54033     /* Centralized function for browser feature detection
54034         - User agent string detection is only used when no good alternative is possible
54035         - Is executed directly for optimal performance
54036     */  
54037     ua = function() {
54038         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
54039             u = nav.userAgent.toLowerCase(),
54040             p = nav.platform.toLowerCase(),
54041             windows = p ? /win/.test(p) : /win/.test(u),
54042             mac = p ? /mac/.test(p) : /mac/.test(u),
54043             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
54044             ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
54045             playerVersion = [0,0,0],
54046             d = null;
54047         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
54048             d = nav.plugins[SHOCKWAVE_FLASH].description;
54049             if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
54050                 plugin = true;
54051                 ie = false; // cascaded feature detection for Internet Explorer
54052                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
54053                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
54054                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
54055                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
54056             }
54057         }
54058         else if (typeof win.ActiveXObject != UNDEF) {
54059             try {
54060                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
54061                 if (a) { // a will return null when ActiveX is disabled
54062                     d = a.GetVariable("$version");
54063                     if (d) {
54064                         ie = true; // cascaded feature detection for Internet Explorer
54065                         d = d.split(" ")[1].split(",");
54066                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
54067                     }
54068                 }
54069             }
54070             catch(e) {}
54071         }
54072         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
54073     }(),
54074     
54075     /* Cross-browser onDomLoad
54076         - Will fire an event as soon as the DOM of a web page is loaded
54077         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
54078         - Regular onload serves as fallback
54079     */ 
54080     onDomLoad = function() {
54081         if (!ua.w3) { return; }
54082         if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
54083             callDomLoadFunctions();
54084         }
54085         if (!isDomLoaded) {
54086             if (typeof doc.addEventListener != UNDEF) {
54087                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
54088             }       
54089             if (ua.ie && ua.win) {
54090                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {
54091                     if (doc.readyState == "complete") {
54092                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
54093                         callDomLoadFunctions();
54094                     }
54095                 });
54096                 if (win == top) { // if not inside an iframe
54097                     (function(){
54098                         if (isDomLoaded) { return; }
54099                         try {
54100                             doc.documentElement.doScroll("left");
54101                         }
54102                         catch(e) {
54103                             setTimeout(arguments.callee, 0);
54104                             return;
54105                         }
54106                         callDomLoadFunctions();
54107                     })();
54108                 }
54109             }
54110             if (ua.wk) {
54111                 (function(){
54112                     if (isDomLoaded) { return; }
54113                     if (!/loaded|complete/.test(doc.readyState)) {
54114                         setTimeout(arguments.callee, 0);
54115                         return;
54116                     }
54117                     callDomLoadFunctions();
54118                 })();
54119             }
54120             addLoadEvent(callDomLoadFunctions);
54121         }
54122     }();
54123     
54124     function callDomLoadFunctions() {
54125         if (isDomLoaded) { return; }
54126         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
54127             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
54128             t.parentNode.removeChild(t);
54129         }
54130         catch (e) { return; }
54131         isDomLoaded = true;
54132         var dl = domLoadFnArr.length;
54133         for (var i = 0; i < dl; i++) {
54134             domLoadFnArr[i]();
54135         }
54136     }
54137     
54138     function addDomLoadEvent(fn) {
54139         if (isDomLoaded) {
54140             fn();
54141         }
54142         else { 
54143             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
54144         }
54145     }
54146     
54147     /* Cross-browser onload
54148         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
54149         - Will fire an event as soon as a web page including all of its assets are loaded 
54150      */
54151     function addLoadEvent(fn) {
54152         if (typeof win.addEventListener != UNDEF) {
54153             win.addEventListener("load", fn, false);
54154         }
54155         else if (typeof doc.addEventListener != UNDEF) {
54156             doc.addEventListener("load", fn, false);
54157         }
54158         else if (typeof win.attachEvent != UNDEF) {
54159             addListener(win, "onload", fn);
54160         }
54161         else if (typeof win.onload == "function") {
54162             var fnOld = win.onload;
54163             win.onload = function() {
54164                 fnOld();
54165                 fn();
54166             };
54167         }
54168         else {
54169             win.onload = fn;
54170         }
54171     }
54172     
54173     /* Main function
54174         - Will preferably execute onDomLoad, otherwise onload (as a fallback)
54175     */
54176     function main() { 
54177         if (plugin) {
54178             testPlayerVersion();
54179         }
54180         else {
54181             matchVersions();
54182         }
54183     }
54184     
54185     /* Detect the Flash Player version for non-Internet Explorer browsers
54186         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
54187           a. Both release and build numbers can be detected
54188           b. Avoid wrong descriptions by corrupt installers provided by Adobe
54189           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
54190         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
54191     */
54192     function testPlayerVersion() {
54193         var b = doc.getElementsByTagName("body")[0];
54194         var o = createElement(OBJECT);
54195         o.setAttribute("type", FLASH_MIME_TYPE);
54196         var t = b.appendChild(o);
54197         if (t) {
54198             var counter = 0;
54199             (function(){
54200                 if (typeof t.GetVariable != UNDEF) {
54201                     var d = t.GetVariable("$version");
54202                     if (d) {
54203                         d = d.split(" ")[1].split(",");
54204                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
54205                     }
54206                 }
54207                 else if (counter < 10) {
54208                     counter++;
54209                     setTimeout(arguments.callee, 10);
54210                     return;
54211                 }
54212                 b.removeChild(o);
54213                 t = null;
54214                 matchVersions();
54215             })();
54216         }
54217         else {
54218             matchVersions();
54219         }
54220     }
54221     
54222     /* Perform Flash Player and SWF version matching; static publishing only
54223     */
54224     function matchVersions() {
54225         var rl = regObjArr.length;
54226         if (rl > 0) {
54227             for (var i = 0; i < rl; i++) { // for each registered object element
54228                 var id = regObjArr[i].id;
54229                 var cb = regObjArr[i].callbackFn;
54230                 var cbObj = {success:false, id:id};
54231                 if (ua.pv[0] > 0) {
54232                     var obj = getElementById(id);
54233                     if (obj) {
54234                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
54235                             setVisibility(id, true);
54236                             if (cb) {
54237                                 cbObj.success = true;
54238                                 cbObj.ref = getObjectById(id);
54239                                 cb(cbObj);
54240                             }
54241                         }
54242                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
54243                             var att = {};
54244                             att.data = regObjArr[i].expressInstall;
54245                             att.width = obj.getAttribute("width") || "0";
54246                             att.height = obj.getAttribute("height") || "0";
54247                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
54248                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
54249                             // parse HTML object param element's name-value pairs
54250                             var par = {};
54251                             var p = obj.getElementsByTagName("param");
54252                             var pl = p.length;
54253                             for (var j = 0; j < pl; j++) {
54254                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {
54255                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");
54256                                 }
54257                             }
54258                             showExpressInstall(att, par, id, cb);
54259                         }
54260                         else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
54261                             displayAltContent(obj);
54262                             if (cb) { cb(cbObj); }
54263                         }
54264                     }
54265                 }
54266                 else {  // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
54267                     setVisibility(id, true);
54268                     if (cb) {
54269                         var o = getObjectById(id); // test whether there is an HTML object element or not
54270                         if (o && typeof o.SetVariable != UNDEF) { 
54271                             cbObj.success = true;
54272                             cbObj.ref = o;
54273                         }
54274                         cb(cbObj);
54275                     }
54276                 }
54277             }
54278         }
54279     }
54280     
54281     function getObjectById(objectIdStr) {
54282         var r = null;
54283         var o = getElementById(objectIdStr);
54284         if (o && o.nodeName == "OBJECT") {
54285             if (typeof o.SetVariable != UNDEF) {
54286                 r = o;
54287             }
54288             else {
54289                 var n = o.getElementsByTagName(OBJECT)[0];
54290                 if (n) {
54291                     r = n;
54292                 }
54293             }
54294         }
54295         return r;
54296     }
54297     
54298     /* Requirements for Adobe Express Install
54299         - only one instance can be active at a time
54300         - fp 6.0.65 or higher
54301         - Win/Mac OS only
54302         - no Webkit engines older than version 312
54303     */
54304     function canExpressInstall() {
54305         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
54306     }
54307     
54308     /* Show the Adobe Express Install dialog
54309         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
54310     */
54311     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
54312         isExpressInstallActive = true;
54313         storedCallbackFn = callbackFn || null;
54314         storedCallbackObj = {success:false, id:replaceElemIdStr};
54315         var obj = getElementById(replaceElemIdStr);
54316         if (obj) {
54317             if (obj.nodeName == "OBJECT") { // static publishing
54318                 storedAltContent = abstractAltContent(obj);
54319                 storedAltContentId = null;
54320             }
54321             else { // dynamic publishing
54322                 storedAltContent = obj;
54323                 storedAltContentId = replaceElemIdStr;
54324             }
54325             att.id = EXPRESS_INSTALL_ID;
54326             if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
54327             if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
54328             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
54329             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
54330                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
54331             if (typeof par.flashvars != UNDEF) {
54332                 par.flashvars += "&" + fv;
54333             }
54334             else {
54335                 par.flashvars = fv;
54336             }
54337             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
54338             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
54339             if (ua.ie && ua.win && obj.readyState != 4) {
54340                 var newObj = createElement("div");
54341                 replaceElemIdStr += "SWFObjectNew";
54342                 newObj.setAttribute("id", replaceElemIdStr);
54343                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
54344                 obj.style.display = "none";
54345                 (function(){
54346                     if (obj.readyState == 4) {
54347                         obj.parentNode.removeChild(obj);
54348                     }
54349                     else {
54350                         setTimeout(arguments.callee, 10);
54351                     }
54352                 })();
54353             }
54354             createSWF(att, par, replaceElemIdStr);
54355         }
54356     }
54357     
54358     /* Functions to abstract and display alternative content
54359     */
54360     function displayAltContent(obj) {
54361         if (ua.ie && ua.win && obj.readyState != 4) {
54362             // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
54363             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
54364             var el = createElement("div");
54365             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
54366             el.parentNode.replaceChild(abstractAltContent(obj), el);
54367             obj.style.display = "none";
54368             (function(){
54369                 if (obj.readyState == 4) {
54370                     obj.parentNode.removeChild(obj);
54371                 }
54372                 else {
54373                     setTimeout(arguments.callee, 10);
54374                 }
54375             })();
54376         }
54377         else {
54378             obj.parentNode.replaceChild(abstractAltContent(obj), obj);
54379         }
54380     } 
54381
54382     function abstractAltContent(obj) {
54383         var ac = createElement("div");
54384         if (ua.win && ua.ie) {
54385             ac.innerHTML = obj.innerHTML;
54386         }
54387         else {
54388             var nestedObj = obj.getElementsByTagName(OBJECT)[0];
54389             if (nestedObj) {
54390                 var c = nestedObj.childNodes;
54391                 if (c) {
54392                     var cl = c.length;
54393                     for (var i = 0; i < cl; i++) {
54394                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
54395                             ac.appendChild(c[i].cloneNode(true));
54396                         }
54397                     }
54398                 }
54399             }
54400         }
54401         return ac;
54402     }
54403     
54404     /* Cross-browser dynamic SWF creation
54405     */
54406     function createSWF(attObj, parObj, id) {
54407         var r, el = getElementById(id);
54408         if (ua.wk && ua.wk < 312) { return r; }
54409         if (el) {
54410             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
54411                 attObj.id = id;
54412             }
54413             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
54414                 var att = "";
54415                 for (var i in attObj) {
54416                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
54417                         if (i.toLowerCase() == "data") {
54418                             parObj.movie = attObj[i];
54419                         }
54420                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
54421                             att += ' class="' + attObj[i] + '"';
54422                         }
54423                         else if (i.toLowerCase() != "classid") {
54424                             att += ' ' + i + '="' + attObj[i] + '"';
54425                         }
54426                     }
54427                 }
54428                 var par = "";
54429                 for (var j in parObj) {
54430                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
54431                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';
54432                     }
54433                 }
54434                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
54435                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
54436                 r = getElementById(attObj.id);  
54437             }
54438             else { // well-behaving browsers
54439                 var o = createElement(OBJECT);
54440                 o.setAttribute("type", FLASH_MIME_TYPE);
54441                 for (var m in attObj) {
54442                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
54443                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
54444                             o.setAttribute("class", attObj[m]);
54445                         }
54446                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
54447                             o.setAttribute(m, attObj[m]);
54448                         }
54449                     }
54450                 }
54451                 for (var n in parObj) {
54452                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
54453                         createObjParam(o, n, parObj[n]);
54454                     }
54455                 }
54456                 el.parentNode.replaceChild(o, el);
54457                 r = o;
54458             }
54459         }
54460         return r;
54461     }
54462     
54463     function createObjParam(el, pName, pValue) {
54464         var p = createElement("param");
54465         p.setAttribute("name", pName);  
54466         p.setAttribute("value", pValue);
54467         el.appendChild(p);
54468     }
54469     
54470     /* Cross-browser SWF removal
54471         - Especially needed to safely and completely remove a SWF in Internet Explorer
54472     */
54473     function removeSWF(id) {
54474         var obj = getElementById(id);
54475         if (obj && obj.nodeName == "OBJECT") {
54476             if (ua.ie && ua.win) {
54477                 obj.style.display = "none";
54478                 (function(){
54479                     if (obj.readyState == 4) {
54480                         removeObjectInIE(id);
54481                     }
54482                     else {
54483                         setTimeout(arguments.callee, 10);
54484                     }
54485                 })();
54486             }
54487             else {
54488                 obj.parentNode.removeChild(obj);
54489             }
54490         }
54491     }
54492     
54493     function removeObjectInIE(id) {
54494         var obj = getElementById(id);
54495         if (obj) {
54496             for (var i in obj) {
54497                 if (typeof obj[i] == "function") {
54498                     obj[i] = null;
54499                 }
54500             }
54501             obj.parentNode.removeChild(obj);
54502         }
54503     }
54504     
54505     /* Functions to optimize JavaScript compression
54506     */
54507     function getElementById(id) {
54508         var el = null;
54509         try {
54510             el = doc.getElementById(id);
54511         }
54512         catch (e) {}
54513         return el;
54514     }
54515     
54516     function createElement(el) {
54517         return doc.createElement(el);
54518     }
54519     
54520     /* Updated attachEvent function for Internet Explorer
54521         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
54522     */  
54523     function addListener(target, eventType, fn) {
54524         target.attachEvent(eventType, fn);
54525         listenersArr[listenersArr.length] = [target, eventType, fn];
54526     }
54527     
54528     /* Flash Player and SWF content version matching
54529     */
54530     function hasPlayerVersion(rv) {
54531         var pv = ua.pv, v = rv.split(".");
54532         v[0] = parseInt(v[0], 10);
54533         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
54534         v[2] = parseInt(v[2], 10) || 0;
54535         return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
54536     }
54537     
54538     /* Cross-browser dynamic CSS creation
54539         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
54540     */  
54541     function createCSS(sel, decl, media, newStyle) {
54542         if (ua.ie && ua.mac) { return; }
54543         var h = doc.getElementsByTagName("head")[0];
54544         if (!h) { return; } // to also support badly authored HTML pages that lack a head element
54545         var m = (media && typeof media == "string") ? media : "screen";
54546         if (newStyle) {
54547             dynamicStylesheet = null;
54548             dynamicStylesheetMedia = null;
54549         }
54550         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
54551             // create dynamic stylesheet + get a global reference to it
54552             var s = createElement("style");
54553             s.setAttribute("type", "text/css");
54554             s.setAttribute("media", m);
54555             dynamicStylesheet = h.appendChild(s);
54556             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
54557                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
54558             }
54559             dynamicStylesheetMedia = m;
54560         }
54561         // add style rule
54562         if (ua.ie && ua.win) {
54563             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
54564                 dynamicStylesheet.addRule(sel, decl);
54565             }
54566         }
54567         else {
54568             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
54569                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
54570             }
54571         }
54572     }
54573     
54574     function setVisibility(id, isVisible) {
54575         if (!autoHideShow) { return; }
54576         var v = isVisible ? "visible" : "hidden";
54577         if (isDomLoaded && getElementById(id)) {
54578             getElementById(id).style.visibility = v;
54579         }
54580         else {
54581             createCSS("#" + id, "visibility:" + v);
54582         }
54583     }
54584
54585     /* Filter to avoid XSS attacks
54586     */
54587     function urlEncodeIfNecessary(s) {
54588         var regex = /[\\\"<>\.;]/;
54589         var hasBadChars = regex.exec(s) != null;
54590         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
54591     }
54592     
54593     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
54594     */
54595     var cleanup = function() {
54596         if (ua.ie && ua.win) {
54597             window.attachEvent("onunload", function() {
54598                 // remove listeners to avoid memory leaks
54599                 var ll = listenersArr.length;
54600                 for (var i = 0; i < ll; i++) {
54601                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
54602                 }
54603                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
54604                 var il = objIdArr.length;
54605                 for (var j = 0; j < il; j++) {
54606                     removeSWF(objIdArr[j]);
54607                 }
54608                 // cleanup library's main closures to avoid memory leaks
54609                 for (var k in ua) {
54610                     ua[k] = null;
54611                 }
54612                 ua = null;
54613                 for (var l in swfobject) {
54614                     swfobject[l] = null;
54615                 }
54616                 swfobject = null;
54617             });
54618         }
54619     }();
54620     
54621     return {
54622         /* Public API
54623             - Reference: http://code.google.com/p/swfobject/wiki/documentation
54624         */ 
54625         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
54626             if (ua.w3 && objectIdStr && swfVersionStr) {
54627                 var regObj = {};
54628                 regObj.id = objectIdStr;
54629                 regObj.swfVersion = swfVersionStr;
54630                 regObj.expressInstall = xiSwfUrlStr;
54631                 regObj.callbackFn = callbackFn;
54632                 regObjArr[regObjArr.length] = regObj;
54633                 setVisibility(objectIdStr, false);
54634             }
54635             else if (callbackFn) {
54636                 callbackFn({success:false, id:objectIdStr});
54637             }
54638         },
54639         
54640         getObjectById: function(objectIdStr) {
54641             if (ua.w3) {
54642                 return getObjectById(objectIdStr);
54643             }
54644         },
54645         
54646         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
54647             var callbackObj = {success:false, id:replaceElemIdStr};
54648             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
54649                 setVisibility(replaceElemIdStr, false);
54650                 addDomLoadEvent(function() {
54651                     widthStr += ""; // auto-convert to string
54652                     heightStr += "";
54653                     var att = {};
54654                     if (attObj && typeof attObj === OBJECT) {
54655                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
54656                             att[i] = attObj[i];
54657                         }
54658                     }
54659                     att.data = swfUrlStr;
54660                     att.width = widthStr;
54661                     att.height = heightStr;
54662                     var par = {}; 
54663                     if (parObj && typeof parObj === OBJECT) {
54664                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
54665                             par[j] = parObj[j];
54666                         }
54667                     }
54668                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {
54669                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
54670                             if (typeof par.flashvars != UNDEF) {
54671                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];
54672                             }
54673                             else {
54674                                 par.flashvars = k + "=" + flashvarsObj[k];
54675                             }
54676                         }
54677                     }
54678                     if (hasPlayerVersion(swfVersionStr)) { // create SWF
54679                         var obj = createSWF(att, par, replaceElemIdStr);
54680                         if (att.id == replaceElemIdStr) {
54681                             setVisibility(replaceElemIdStr, true);
54682                         }
54683                         callbackObj.success = true;
54684                         callbackObj.ref = obj;
54685                     }
54686                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
54687                         att.data = xiSwfUrlStr;
54688                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);
54689                         return;
54690                     }
54691                     else { // show alternative content
54692                         setVisibility(replaceElemIdStr, true);
54693                     }
54694                     if (callbackFn) { callbackFn(callbackObj); }
54695                 });
54696             }
54697             else if (callbackFn) { callbackFn(callbackObj); }
54698         },
54699         
54700         switchOffAutoHideShow: function() {
54701             autoHideShow = false;
54702         },
54703         
54704         ua: ua,
54705         
54706         getFlashPlayerVersion: function() {
54707             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
54708         },
54709         
54710         hasFlashPlayerVersion: hasPlayerVersion,
54711         
54712         createSWF: function(attObj, parObj, replaceElemIdStr) {
54713             if (ua.w3) {
54714                 return createSWF(attObj, parObj, replaceElemIdStr);
54715             }
54716             else {
54717                 return undefined;
54718             }
54719         },
54720         
54721         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
54722             if (ua.w3 && canExpressInstall()) {
54723                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);
54724             }
54725         },
54726         
54727         removeSWF: function(objElemIdStr) {
54728             if (ua.w3) {
54729                 removeSWF(objElemIdStr);
54730             }
54731         },
54732         
54733         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
54734             if (ua.w3) {
54735                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);
54736             }
54737         },
54738         
54739         addDomLoadEvent: addDomLoadEvent,
54740         
54741         addLoadEvent: addLoadEvent,
54742         
54743         getQueryParamValue: function(param) {
54744             var q = doc.location.search || doc.location.hash;
54745             if (q) {
54746                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
54747                 if (param == null) {
54748                     return urlEncodeIfNecessary(q);
54749                 }
54750                 var pairs = q.split("&");
54751                 for (var i = 0; i < pairs.length; i++) {
54752                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
54753                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
54754                     }
54755                 }
54756             }
54757             return "";
54758         },
54759         
54760         // For internal usage only
54761         expressInstallCallback: function() {
54762             if (isExpressInstallActive) {
54763                 var obj = getElementById(EXPRESS_INSTALL_ID);
54764                 if (obj && storedAltContent) {
54765                     obj.parentNode.replaceChild(storedAltContent, obj);
54766                     if (storedAltContentId) {
54767                         setVisibility(storedAltContentId, true);
54768                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
54769                     }
54770                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
54771                 }
54772                 isExpressInstallActive = false;
54773             } 
54774         }
54775     };
54776 }();
54777 /**
54778  * @class Ext.FlashComponent
54779  * @extends Ext.BoxComponent
54780  * @constructor
54781  * @xtype flash
54782  */
54783 Ext.FlashComponent = Ext.extend(Ext.BoxComponent, {
54784     /**
54785      * @cfg {String} flashVersion
54786      * Indicates the version the flash content was published for. Defaults to <tt>'9.0.115'</tt>.
54787      */
54788     flashVersion : '9.0.115',
54789
54790     /**
54791      * @cfg {String} backgroundColor
54792      * The background color of the chart. Defaults to <tt>'#ffffff'</tt>.
54793      */
54794     backgroundColor: '#ffffff',
54795
54796     /**
54797      * @cfg {String} wmode
54798      * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
54799      */
54800     wmode: 'opaque',
54801
54802     /**
54803      * @cfg {Object} flashVars
54804      * A set of key value pairs to be passed to the flash object as flash variables. Defaults to <tt>undefined</tt>.
54805      */
54806     flashVars: undefined,
54807
54808     /**
54809      * @cfg {Object} flashParams
54810      * A set of key value pairs to be passed to the flash object as parameters. Possible parameters can be found here:
54811      * http://kb2.adobe.com/cps/127/tn_12701.html Defaults to <tt>undefined</tt>.
54812      */
54813     flashParams: undefined,
54814
54815     /**
54816      * @cfg {String} url
54817      * The URL of the chart to include. Defaults to <tt>undefined</tt>.
54818      */
54819     url: undefined,
54820     swfId : undefined,
54821     swfWidth: '100%',
54822     swfHeight: '100%',
54823
54824     /**
54825      * @cfg {Boolean} expressInstall
54826      * True to prompt the user to install flash if not installed. Note that this uses
54827      * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
54828      */
54829     expressInstall: false,
54830
54831     initComponent : function(){
54832         Ext.FlashComponent.superclass.initComponent.call(this);
54833
54834         this.addEvents(
54835             /**
54836              * @event initialize
54837              *
54838              * @param {Chart} this
54839              */
54840             'initialize'
54841         );
54842     },
54843
54844     onRender : function(){
54845         Ext.FlashComponent.superclass.onRender.apply(this, arguments);
54846
54847         var params = Ext.apply({
54848             allowScriptAccess: 'always',
54849             bgcolor: this.backgroundColor,
54850             wmode: this.wmode
54851         }, this.flashParams), vars = Ext.apply({
54852             allowedDomain: document.location.hostname,
54853             YUISwfId: this.getId(),
54854             YUIBridgeCallback: 'Ext.FlashEventProxy.onEvent'
54855         }, this.flashVars);
54856
54857         new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
54858             this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);
54859
54860         this.swf = Ext.getDom(this.id);
54861         this.el = Ext.get(this.swf);
54862     },
54863
54864     getSwfId : function(){
54865         return this.swfId || (this.swfId = "extswf" + (++Ext.Component.AUTO_ID));
54866     },
54867
54868     getId : function(){
54869         return this.id || (this.id = "extflashcmp" + (++Ext.Component.AUTO_ID));
54870     },
54871
54872     onFlashEvent : function(e){
54873         switch(e.type){
54874             case "swfReady":
54875                 this.initSwf();
54876                 return;
54877             case "log":
54878                 return;
54879         }
54880         e.component = this;
54881         this.fireEvent(e.type.toLowerCase().replace(/event$/, ''), e);
54882     },
54883
54884     initSwf : function(){
54885         this.onSwfReady(!!this.isInitialized);
54886         this.isInitialized = true;
54887         this.fireEvent('initialize', this);
54888     },
54889
54890     beforeDestroy: function(){
54891         if(this.rendered){
54892             swfobject.removeSWF(this.swf.id);
54893         }
54894         Ext.FlashComponent.superclass.beforeDestroy.call(this);
54895     },
54896
54897     onSwfReady : Ext.emptyFn
54898 });
54899
54900 /**
54901  * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
54902  * @static
54903  * @type String
54904  */
54905 Ext.FlashComponent.EXPRESS_INSTALL_URL = 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf';
54906
54907 Ext.reg('flash', Ext.FlashComponent);/**
54908  * @class Ext.FlashProxy
54909  * @singleton
54910  */
54911 Ext.FlashEventProxy = {
54912     onEvent : function(id, e){
54913         var fp = Ext.getCmp(id);
54914         if(fp){
54915             fp.onFlashEvent(e);
54916         }else{
54917             arguments.callee.defer(10, this, [id, e]);
54918         }
54919     }
54920 };/**
54921  * @class Ext.chart.Chart
54922  * @extends Ext.FlashComponent
54923  * The Ext.chart package provides the capability to visualize data with flash based charting.
54924  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.
54925  * To change the look and feel of a chart, see the {@link #chartStyle} and {@link #extraStyle} config options.
54926  * @constructor
54927  * @xtype chart
54928  */
54929
54930  Ext.chart.Chart = Ext.extend(Ext.FlashComponent, {
54931     refreshBuffer: 100,
54932
54933     /**
54934      * @cfg {String} backgroundColor
54935      * @hide
54936      */
54937
54938     /**
54939      * @cfg {Object} chartStyle
54940      * Sets styles for this chart. This contains default styling, so modifying this property will <b>override</b>
54941      * the built in styles of the chart. Use {@link #extraStyle} to add customizations to the default styling.
54942      */
54943     chartStyle: {
54944         padding: 10,
54945         animationEnabled: true,
54946         font: {
54947             name: 'Tahoma',
54948             color: 0x444444,
54949             size: 11
54950         },
54951         dataTip: {
54952             padding: 5,
54953             border: {
54954                 color: 0x99bbe8,
54955                 size:1
54956             },
54957             background: {
54958                 color: 0xDAE7F6,
54959                 alpha: .9
54960             },
54961             font: {
54962                 name: 'Tahoma',
54963                 color: 0x15428B,
54964                 size: 10,
54965                 bold: true
54966             }
54967         }
54968     },
54969
54970     /**
54971      * @cfg {String} url
54972      * The url to load the chart from. This defaults to Ext.chart.Chart.CHART_URL, which should
54973      * be modified to point to the local charts resource.
54974      */
54975
54976     /**
54977      * @cfg {Object} extraStyle
54978      * Contains extra styles that will be added or overwritten to the default chartStyle. Defaults to <tt>null</tt>.
54979      * For a detailed list of the options available, visit the YUI Charts site
54980      * at <a href="http://developer.yahoo.com/yui/charts/#basicstyles">http://developer.yahoo.com/yui/charts/#basicstyles</a><br/>
54981      * Some of the options availabe:<br />
54982      * <ul style="padding:5px;padding-left:16px;list-style-type:inherit;">
54983      * <li><b>padding</b> - The space around the edge of the chart's contents. Padding does not increase the size of the chart.</li>
54984      * <li><b>animationEnabled</b> - A Boolean value that specifies whether marker animations are enabled or not. Enabled by default.</li>
54985      * <li><b>font</b> - An Object defining the font style to be used in the chart. Defaults to <tt>{ name: 'Tahoma', color: 0x444444, size: 11 }</tt><br/>
54986      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
54987      *      <li><b>name</b> - font name</li>
54988      *      <li><b>color</b> - font color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
54989      *      <li><b>size</b> - font size in points (numeric portion only, ie: 11)</li>
54990      *      <li><b>bold</b> - boolean</li>
54991      *      <li><b>italic</b> - boolean</li>
54992      *      <li><b>underline</b> - boolean</li>
54993      *  </ul>
54994      * </li>
54995      * <li><b>border</b> - An object defining the border style around the chart. The chart itself will decrease in dimensions to accomodate the border.<br/>
54996      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
54997      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
54998      *      <li><b>size</b> - border size in pixels (numeric portion only, ie: 1)</li>
54999      *  </ul>
55000      * </li>
55001      * <li><b>background</b> - An object defining the background style of the chart.<br/>
55002      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55003      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
55004      *      <li><b>image</b> - an image URL. May be relative to the current document or absolute.</li>
55005      *  </ul>
55006      * </li>
55007      * <li><b>legend</b> - An object defining the legend style<br/>
55008      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55009      *      <li><b>display</b> - location of the legend. Possible values are "none", "left", "right", "top", and "bottom".</li>
55010      *      <li><b>spacing</b> - an image URL. May be relative to the current document or absolute.</li>
55011      *      <li><b>padding, border, background, font</b> - same options as described above.</li>
55012      *  </ul></li>
55013      * <li><b>dataTip</b> - An object defining the style of the data tip (tooltip).<br/>
55014      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55015      *      <li><b>padding, border, background, font</b> - same options as described above.</li>
55016      *  </ul></li>
55017      * <li><b>xAxis and yAxis</b> - An object defining the style of the style of either axis.<br/>
55018      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55019      *      <li><b>color</b> - same option as described above.</li>
55020      *      <li><b>size</b> - same option as described above.</li>
55021      *      <li><b>showLabels</b> - boolean</li>
55022      *      <li><b>labelRotation</b> - a value in degrees from -90 through 90. Default is zero.</li>
55023      *  </ul></li>
55024      * <li><b>majorGridLines and minorGridLines</b> - An object defining the style of the style of the grid lines.<br/>
55025      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55026      *      <li><b>color, size</b> - same options as described above.</li>
55027      *  </ul></li></li>
55028      * <li><b>zeroGridLine</b> - An object defining the style of the style of the zero grid line.<br/>
55029      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55030      *      <li><b>color, size</b> - same options as described above.</li>
55031      *  </ul></li></li>
55032      * <li><b>majorTicks and minorTicks</b> - An object defining the style of the style of ticks in the chart.<br/>
55033      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55034      *      <li><b>color, size</b> - same options as described above.</li>
55035      *      <li><b>length</b> - the length of each tick in pixels extending from the axis.</li>
55036      *      <li><b>display</b> - how the ticks are drawn. Possible values are "none", "inside", "outside", and "cross".</li>
55037      *  </ul></li></li>
55038      * </ul>
55039      */
55040     extraStyle: null,
55041
55042     /**
55043      * @cfg {Object} seriesStyles
55044      * Contains styles to apply to the series after a refresh. Defaults to <tt>null</tt>.
55045      */
55046     seriesStyles: null,
55047
55048     /**
55049      * @cfg {Boolean} disableCaching
55050      * True to add a "cache buster" to the end of the chart url. Defaults to true for Opera and IE.
55051      */
55052     disableCaching: Ext.isIE || Ext.isOpera,
55053     disableCacheParam: '_dc',
55054
55055     initComponent : function(){
55056         Ext.chart.Chart.superclass.initComponent.call(this);
55057         if(!this.url){
55058             this.url = Ext.chart.Chart.CHART_URL;
55059         }
55060         if(this.disableCaching){
55061             this.url = Ext.urlAppend(this.url, String.format('{0}={1}', this.disableCacheParam, new Date().getTime()));
55062         }
55063         this.addEvents(
55064             'itemmouseover',
55065             'itemmouseout',
55066             'itemclick',
55067             'itemdoubleclick',
55068             'itemdragstart',
55069             'itemdrag',
55070             'itemdragend',
55071             /**
55072              * @event beforerefresh
55073              * Fires before a refresh to the chart data is called.  If the beforerefresh handler returns
55074              * <tt>false</tt> the {@link #refresh} action will be cancelled.
55075              * @param {Chart} this
55076              */
55077             'beforerefresh',
55078             /**
55079              * @event refresh
55080              * Fires after the chart data has been refreshed.
55081              * @param {Chart} this
55082              */
55083             'refresh'
55084         );
55085         this.store = Ext.StoreMgr.lookup(this.store);
55086     },
55087
55088     /**
55089      * Sets a single style value on the Chart instance.
55090      *
55091      * @param name {String} Name of the Chart style value to change.
55092      * @param value {Object} New value to pass to the Chart style.
55093      */
55094      setStyle: function(name, value){
55095          this.swf.setStyle(name, Ext.encode(value));
55096      },
55097
55098     /**
55099      * Resets all styles on the Chart instance.
55100      *
55101      * @param styles {Object} Initializer for all Chart styles.
55102      */
55103     setStyles: function(styles){
55104         this.swf.setStyles(Ext.encode(styles));
55105     },
55106
55107     /**
55108      * Sets the styles on all series in the Chart.
55109      *
55110      * @param styles {Array} Initializer for all Chart series styles.
55111      */
55112     setSeriesStyles: function(styles){
55113         this.seriesStyles = styles;
55114         var s = [];
55115         Ext.each(styles, function(style){
55116             s.push(Ext.encode(style));
55117         });
55118         this.swf.setSeriesStyles(s);
55119     },
55120
55121     setCategoryNames : function(names){
55122         this.swf.setCategoryNames(names);
55123     },
55124
55125     setLegendRenderer : function(fn, scope){
55126         var chart = this;
55127         scope = scope || chart;
55128         chart.removeFnProxy(chart.legendFnName);
55129         chart.legendFnName = chart.createFnProxy(function(name){
55130             return fn.call(scope, name);
55131         });
55132         chart.swf.setLegendLabelFunction(chart.legendFnName);
55133     },
55134
55135     setTipRenderer : function(fn, scope){
55136         var chart = this;
55137         scope = scope || chart;
55138         chart.removeFnProxy(chart.tipFnName);
55139         chart.tipFnName = chart.createFnProxy(function(item, index, series){
55140             var record = chart.store.getAt(index);
55141             return fn.call(scope, chart, record, index, series);
55142         });
55143         chart.swf.setDataTipFunction(chart.tipFnName);
55144     },
55145
55146     setSeries : function(series){
55147         this.series = series;
55148         this.refresh();
55149     },
55150
55151     /**
55152      * Changes the data store bound to this chart and refreshes it.
55153      * @param {Store} store The store to bind to this chart
55154      */
55155     bindStore : function(store, initial){
55156         if(!initial && this.store){
55157             if(store !== this.store && this.store.autoDestroy){
55158                 this.store.destroy();
55159             }else{
55160                 this.store.un("datachanged", this.refresh, this);
55161                 this.store.un("add", this.delayRefresh, this);
55162                 this.store.un("remove", this.delayRefresh, this);
55163                 this.store.un("update", this.delayRefresh, this);
55164                 this.store.un("clear", this.refresh, this);
55165             }
55166         }
55167         if(store){
55168             store = Ext.StoreMgr.lookup(store);
55169             store.on({
55170                 scope: this,
55171                 datachanged: this.refresh,
55172                 add: this.delayRefresh,
55173                 remove: this.delayRefresh,
55174                 update: this.delayRefresh,
55175                 clear: this.refresh
55176             });
55177         }
55178         this.store = store;
55179         if(store && !initial){
55180             this.refresh();
55181         }
55182     },
55183
55184     onSwfReady : function(isReset){
55185         Ext.chart.Chart.superclass.onSwfReady.call(this, isReset);
55186         var ref;
55187         this.swf.setType(this.type);
55188
55189         if(this.chartStyle){
55190             this.setStyles(Ext.apply({}, this.extraStyle, this.chartStyle));
55191         }
55192
55193         if(this.categoryNames){
55194             this.setCategoryNames(this.categoryNames);
55195         }
55196
55197         if(this.tipRenderer){
55198             ref = this.getFunctionRef(this.tipRenderer);
55199             this.setTipRenderer(ref.fn, ref.scope);
55200         }
55201         if(this.legendRenderer){
55202             ref = this.getFunctionRef(this.legendRenderer);
55203             this.setLegendRenderer(ref.fn, ref.scope);
55204         }
55205         if(!isReset){
55206             this.bindStore(this.store, true);
55207         }
55208         this.refresh.defer(10, this);
55209     },
55210
55211     delayRefresh : function(){
55212         if(!this.refreshTask){
55213             this.refreshTask = new Ext.util.DelayedTask(this.refresh, this);
55214         }
55215         this.refreshTask.delay(this.refreshBuffer);
55216     },
55217
55218     refresh : function(){
55219         if(this.fireEvent('beforerefresh', this) !== false){
55220             var styleChanged = false;
55221             // convert the store data into something YUI charts can understand
55222             var data = [], rs = this.store.data.items;
55223             for(var j = 0, len = rs.length; j < len; j++){
55224                 data[j] = rs[j].data;
55225             }
55226             //make a copy of the series definitions so that we aren't
55227             //editing them directly.
55228             var dataProvider = [];
55229             var seriesCount = 0;
55230             var currentSeries = null;
55231             var i = 0;
55232             if(this.series){
55233                 seriesCount = this.series.length;
55234                 for(i = 0; i < seriesCount; i++){
55235                     currentSeries = this.series[i];
55236                     var clonedSeries = {};
55237                     for(var prop in currentSeries){
55238                         if(prop == "style" && currentSeries.style !== null){
55239                             clonedSeries.style = Ext.encode(currentSeries.style);
55240                             styleChanged = true;
55241                             //we don't want to modify the styles again next time
55242                             //so null out the style property.
55243                             // this causes issues
55244                             // currentSeries.style = null;
55245                         } else{
55246                             clonedSeries[prop] = currentSeries[prop];
55247                         }
55248                     }
55249                     dataProvider.push(clonedSeries);
55250                 }
55251             }
55252
55253             if(seriesCount > 0){
55254                 for(i = 0; i < seriesCount; i++){
55255                     currentSeries = dataProvider[i];
55256                     if(!currentSeries.type){
55257                         currentSeries.type = this.type;
55258                     }
55259                     currentSeries.dataProvider = data;
55260                 }
55261             } else{
55262                 dataProvider.push({type: this.type, dataProvider: data});
55263             }
55264             this.swf.setDataProvider(dataProvider);
55265             if(this.seriesStyles){
55266                 this.setSeriesStyles(this.seriesStyles);
55267             }
55268             this.fireEvent('refresh', this);
55269         }
55270     },
55271
55272     // private
55273     createFnProxy : function(fn){
55274         var fnName = 'extFnProxy' + (++Ext.chart.Chart.PROXY_FN_ID);
55275         Ext.chart.Chart.proxyFunction[fnName] = fn;
55276         return 'Ext.chart.Chart.proxyFunction.' + fnName;
55277     },
55278
55279     // private
55280     removeFnProxy : function(fn){
55281         if(!Ext.isEmpty(fn)){
55282             fn = fn.replace('Ext.chart.Chart.proxyFunction.', '');
55283             delete Ext.chart.Chart.proxyFunction[fn];
55284         }
55285     },
55286
55287     // private
55288     getFunctionRef : function(val){
55289         if(Ext.isFunction(val)){
55290             return {
55291                 fn: val,
55292                 scope: this
55293             };
55294         }else{
55295             return {
55296                 fn: val.fn,
55297                 scope: val.scope || this
55298             }
55299         }
55300     },
55301
55302     // private
55303     onDestroy: function(){
55304         if (this.refreshTask && this.refreshTask.cancel){
55305             this.refreshTask.cancel();
55306         }
55307         Ext.chart.Chart.superclass.onDestroy.call(this);
55308         this.bindStore(null);
55309         this.removeFnProxy(this.tipFnName);
55310         this.removeFnProxy(this.legendFnName);
55311     }
55312 });
55313 Ext.reg('chart', Ext.chart.Chart);
55314 Ext.chart.Chart.PROXY_FN_ID = 0;
55315 Ext.chart.Chart.proxyFunction = {};
55316
55317 /**
55318  * Sets the url to load the chart from. This should be set to a local resource.
55319  * @static
55320  * @type String
55321  */
55322 Ext.chart.Chart.CHART_URL = 'http:/' + '/yui.yahooapis.com/2.8.0/build/charts/assets/charts.swf';
55323
55324 /**
55325  * @class Ext.chart.PieChart
55326  * @extends Ext.chart.Chart
55327  * @constructor
55328  * @xtype piechart
55329  */
55330 Ext.chart.PieChart = Ext.extend(Ext.chart.Chart, {
55331     type: 'pie',
55332
55333     onSwfReady : function(isReset){
55334         Ext.chart.PieChart.superclass.onSwfReady.call(this, isReset);
55335
55336         this.setDataField(this.dataField);
55337         this.setCategoryField(this.categoryField);
55338     },
55339
55340     setDataField : function(field){
55341         this.dataField = field;
55342         this.swf.setDataField(field);
55343     },
55344
55345     setCategoryField : function(field){
55346         this.categoryField = field;
55347         this.swf.setCategoryField(field);
55348     }
55349 });
55350 Ext.reg('piechart', Ext.chart.PieChart);
55351
55352 /**
55353  * @class Ext.chart.CartesianChart
55354  * @extends Ext.chart.Chart
55355  * @constructor
55356  * @xtype cartesianchart
55357  */
55358 Ext.chart.CartesianChart = Ext.extend(Ext.chart.Chart, {
55359     onSwfReady : function(isReset){
55360         Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);
55361         this.labelFn = [];
55362         if(this.xField){
55363             this.setXField(this.xField);
55364         }
55365         if(this.yField){
55366             this.setYField(this.yField);
55367         }
55368         if(this.xAxis){
55369             this.setXAxis(this.xAxis);
55370         }
55371         if(this.xAxes){
55372             this.setXAxes(this.xAxes);
55373         }
55374         if(this.yAxis){
55375             this.setYAxis(this.yAxis);
55376         }
55377         if(this.yAxes){
55378             this.setYAxes(this.yAxes);
55379         }
55380         if(Ext.isDefined(this.constrainViewport)){
55381             this.swf.setConstrainViewport(this.constrainViewport);
55382         }
55383     },
55384
55385     setXField : function(value){
55386         this.xField = value;
55387         this.swf.setHorizontalField(value);
55388     },
55389
55390     setYField : function(value){
55391         this.yField = value;
55392         this.swf.setVerticalField(value);
55393     },
55394
55395     setXAxis : function(value){
55396         this.xAxis = this.createAxis('xAxis', value);
55397         this.swf.setHorizontalAxis(this.xAxis);
55398     },
55399
55400     setXAxes : function(value){
55401         var axis;
55402         for(var i = 0; i < value.length; i++) {
55403             axis = this.createAxis('xAxis' + i, value[i]);
55404             this.swf.setHorizontalAxis(axis);
55405         }
55406     },
55407
55408     setYAxis : function(value){
55409         this.yAxis = this.createAxis('yAxis', value);
55410         this.swf.setVerticalAxis(this.yAxis);
55411     },
55412
55413     setYAxes : function(value){
55414         var axis;
55415         for(var i = 0; i < value.length; i++) {
55416             axis = this.createAxis('yAxis' + i, value[i]);
55417             this.swf.setVerticalAxis(axis);
55418         }
55419     },
55420
55421     createAxis : function(axis, value){
55422         var o = Ext.apply({}, value),
55423             ref,
55424             old;
55425
55426         if(this[axis]){
55427             old = this[axis].labelFunction;
55428             this.removeFnProxy(old);
55429             this.labelFn.remove(old);
55430         }
55431         if(o.labelRenderer){
55432             ref = this.getFunctionRef(o.labelRenderer);
55433             o.labelFunction = this.createFnProxy(function(v){
55434                 return ref.fn.call(ref.scope, v);
55435             });
55436             delete o.labelRenderer;
55437             this.labelFn.push(o.labelFunction);
55438         }
55439         if(axis.indexOf('xAxis') > -1 && o.position == 'left'){
55440             o.position = 'bottom';
55441         }
55442         return o;
55443     },
55444
55445     onDestroy : function(){
55446         Ext.chart.CartesianChart.superclass.onDestroy.call(this);
55447         Ext.each(this.labelFn, function(fn){
55448             this.removeFnProxy(fn);
55449         }, this);
55450     }
55451 });
55452 Ext.reg('cartesianchart', Ext.chart.CartesianChart);
55453
55454 /**
55455  * @class Ext.chart.LineChart
55456  * @extends Ext.chart.CartesianChart
55457  * @constructor
55458  * @xtype linechart
55459  */
55460 Ext.chart.LineChart = Ext.extend(Ext.chart.CartesianChart, {
55461     type: 'line'
55462 });
55463 Ext.reg('linechart', Ext.chart.LineChart);
55464
55465 /**
55466  * @class Ext.chart.ColumnChart
55467  * @extends Ext.chart.CartesianChart
55468  * @constructor
55469  * @xtype columnchart
55470  */
55471 Ext.chart.ColumnChart = Ext.extend(Ext.chart.CartesianChart, {
55472     type: 'column'
55473 });
55474 Ext.reg('columnchart', Ext.chart.ColumnChart);
55475
55476 /**
55477  * @class Ext.chart.StackedColumnChart
55478  * @extends Ext.chart.CartesianChart
55479  * @constructor
55480  * @xtype stackedcolumnchart
55481  */
55482 Ext.chart.StackedColumnChart = Ext.extend(Ext.chart.CartesianChart, {
55483     type: 'stackcolumn'
55484 });
55485 Ext.reg('stackedcolumnchart', Ext.chart.StackedColumnChart);
55486
55487 /**
55488  * @class Ext.chart.BarChart
55489  * @extends Ext.chart.CartesianChart
55490  * @constructor
55491  * @xtype barchart
55492  */
55493 Ext.chart.BarChart = Ext.extend(Ext.chart.CartesianChart, {
55494     type: 'bar'
55495 });
55496 Ext.reg('barchart', Ext.chart.BarChart);
55497
55498 /**
55499  * @class Ext.chart.StackedBarChart
55500  * @extends Ext.chart.CartesianChart
55501  * @constructor
55502  * @xtype stackedbarchart
55503  */
55504 Ext.chart.StackedBarChart = Ext.extend(Ext.chart.CartesianChart, {
55505     type: 'stackbar'
55506 });
55507 Ext.reg('stackedbarchart', Ext.chart.StackedBarChart);
55508
55509
55510
55511 /**
55512  * @class Ext.chart.Axis
55513  * Defines a CartesianChart's vertical or horizontal axis.
55514  * @constructor
55515  */
55516 Ext.chart.Axis = function(config){
55517     Ext.apply(this, config);
55518 };
55519
55520 Ext.chart.Axis.prototype =
55521 {
55522     /**
55523      * The type of axis.
55524      *
55525      * @property type
55526      * @type String
55527      */
55528     type: null,
55529
55530     /**
55531      * The direction in which the axis is drawn. May be "horizontal" or "vertical".
55532      *
55533      * @property orientation
55534      * @type String
55535      */
55536     orientation: "horizontal",
55537
55538     /**
55539      * If true, the items on the axis will be drawn in opposite direction.
55540      *
55541      * @property reverse
55542      * @type Boolean
55543      */
55544     reverse: false,
55545
55546     /**
55547      * A string reference to the globally-accessible function that may be called to
55548      * determine each of the label values for this axis.
55549      *
55550      * @property labelFunction
55551      * @type String
55552      */
55553     labelFunction: null,
55554
55555     /**
55556      * If true, labels that overlap previously drawn labels on the axis will be hidden.
55557      *
55558      * @property hideOverlappingLabels
55559      * @type Boolean
55560      */
55561     hideOverlappingLabels: true,
55562
55563     /**
55564      * The space, in pixels, between labels on an axis.
55565      *
55566      * @property labelSpacing
55567      * @type Number
55568      */
55569     labelSpacing: 2
55570 };
55571
55572 /**
55573  * @class Ext.chart.NumericAxis
55574  * @extends Ext.chart.Axis
55575  * A type of axis whose units are measured in numeric values.
55576  * @constructor
55577  */
55578 Ext.chart.NumericAxis = Ext.extend(Ext.chart.Axis, {
55579     type: "numeric",
55580
55581     /**
55582      * The minimum value drawn by the axis. If not set explicitly, the axis
55583      * minimum will be calculated automatically.
55584      *
55585      * @property minimum
55586      * @type Number
55587      */
55588     minimum: NaN,
55589
55590     /**
55591      * The maximum value drawn by the axis. If not set explicitly, the axis
55592      * maximum will be calculated automatically.
55593      *
55594      * @property maximum
55595      * @type Number
55596      */
55597     maximum: NaN,
55598
55599     /**
55600      * The spacing between major intervals on this axis.
55601      *
55602      * @property majorUnit
55603      * @type Number
55604      */
55605     majorUnit: NaN,
55606
55607     /**
55608      * The spacing between minor intervals on this axis.
55609      *
55610      * @property minorUnit
55611      * @type Number
55612      */
55613     minorUnit: NaN,
55614
55615     /**
55616      * If true, the labels, ticks, gridlines, and other objects will snap to the
55617      * nearest major or minor unit. If false, their position will be based on
55618      * the minimum value.
55619      *
55620      * @property snapToUnits
55621      * @type Boolean
55622      */
55623     snapToUnits: true,
55624
55625     /**
55626      * If true, and the bounds are calculated automatically, either the minimum
55627      * or maximum will be set to zero.
55628      *
55629      * @property alwaysShowZero
55630      * @type Boolean
55631      */
55632     alwaysShowZero: true,
55633
55634     /**
55635      * The scaling algorithm to use on this axis. May be "linear" or
55636      * "logarithmic".
55637      *
55638      * @property scale
55639      * @type String
55640      */
55641     scale: "linear",
55642
55643     /**
55644      * Indicates whether to round the major unit.
55645      *
55646      * @property roundMajorUnit
55647      * @type Boolean
55648      */
55649     roundMajorUnit: true,
55650
55651     /**
55652      * Indicates whether to factor in the size of the labels when calculating a
55653      * major unit.
55654      *
55655      * @property calculateByLabelSize
55656      * @type Boolean
55657      */
55658     calculateByLabelSize: true,
55659
55660     /**
55661      * Indicates the position of the axis relative to the chart
55662      *
55663      * @property position
55664      * @type String
55665      */
55666     position: 'left',
55667
55668     /**
55669      * Indicates whether to extend maximum beyond data's maximum to the nearest
55670      * majorUnit.
55671      *
55672      * @property adjustMaximumByMajorUnit
55673      * @type Boolean
55674      */
55675     adjustMaximumByMajorUnit: true,
55676
55677     /**
55678      * Indicates whether to extend the minimum beyond data's minimum to the
55679      * nearest majorUnit.
55680      *
55681      * @property adjustMinimumByMajorUnit
55682      * @type Boolean
55683      */
55684     adjustMinimumByMajorUnit: true
55685
55686 });
55687
55688 /**
55689  * @class Ext.chart.TimeAxis
55690  * @extends Ext.chart.Axis
55691  * A type of axis whose units are measured in time-based values.
55692  * @constructor
55693  */
55694 Ext.chart.TimeAxis = Ext.extend(Ext.chart.Axis, {
55695     type: "time",
55696
55697     /**
55698      * The minimum value drawn by the axis. If not set explicitly, the axis
55699      * minimum will be calculated automatically.
55700      *
55701      * @property minimum
55702      * @type Date
55703      */
55704     minimum: null,
55705
55706     /**
55707      * The maximum value drawn by the axis. If not set explicitly, the axis
55708      * maximum will be calculated automatically.
55709      *
55710      * @property maximum
55711      * @type Number
55712      */
55713     maximum: null,
55714
55715     /**
55716      * The spacing between major intervals on this axis.
55717      *
55718      * @property majorUnit
55719      * @type Number
55720      */
55721     majorUnit: NaN,
55722
55723     /**
55724      * The time unit used by the majorUnit.
55725      *
55726      * @property majorTimeUnit
55727      * @type String
55728      */
55729     majorTimeUnit: null,
55730
55731     /**
55732      * The spacing between minor intervals on this axis.
55733      *
55734      * @property majorUnit
55735      * @type Number
55736      */
55737     minorUnit: NaN,
55738
55739     /**
55740      * The time unit used by the minorUnit.
55741      *
55742      * @property majorTimeUnit
55743      * @type String
55744      */
55745     minorTimeUnit: null,
55746
55747     /**
55748      * If true, the labels, ticks, gridlines, and other objects will snap to the
55749      * nearest major or minor unit. If false, their position will be based on
55750      * the minimum value.
55751      *
55752      * @property snapToUnits
55753      * @type Boolean
55754      */
55755     snapToUnits: true,
55756
55757     /**
55758      * Series that are stackable will only stack when this value is set to true.
55759      *
55760      * @property stackingEnabled
55761      * @type Boolean
55762      */
55763     stackingEnabled: false,
55764
55765     /**
55766      * Indicates whether to factor in the size of the labels when calculating a
55767      * major unit.
55768      *
55769      * @property calculateByLabelSize
55770      * @type Boolean
55771      */
55772     calculateByLabelSize: true
55773
55774 });
55775
55776 /**
55777  * @class Ext.chart.CategoryAxis
55778  * @extends Ext.chart.Axis
55779  * A type of axis that displays items in categories.
55780  * @constructor
55781  */
55782 Ext.chart.CategoryAxis = Ext.extend(Ext.chart.Axis, {
55783     type: "category",
55784
55785     /**
55786      * A list of category names to display along this axis.
55787      *
55788      * @property categoryNames
55789      * @type Array
55790      */
55791     categoryNames: null,
55792
55793     /**
55794      * Indicates whether or not to calculate the number of categories (ticks and
55795      * labels) when there is not enough room to display all labels on the axis.
55796      * If set to true, the axis will determine the number of categories to plot.
55797      * If not, all categories will be plotted.
55798      *
55799      * @property calculateCategoryCount
55800      * @type Boolean
55801      */
55802     calculateCategoryCount: false
55803
55804 });
55805
55806 /**
55807  * @class Ext.chart.Series
55808  * Series class for the charts widget.
55809  * @constructor
55810  */
55811 Ext.chart.Series = function(config) { Ext.apply(this, config); };
55812
55813 Ext.chart.Series.prototype =
55814 {
55815     /**
55816      * The type of series.
55817      *
55818      * @property type
55819      * @type String
55820      */
55821     type: null,
55822
55823     /**
55824      * The human-readable name of the series.
55825      *
55826      * @property displayName
55827      * @type String
55828      */
55829     displayName: null
55830 };
55831
55832 /**
55833  * @class Ext.chart.CartesianSeries
55834  * @extends Ext.chart.Series
55835  * CartesianSeries class for the charts widget.
55836  * @constructor
55837  */
55838 Ext.chart.CartesianSeries = Ext.extend(Ext.chart.Series, {
55839     /**
55840      * The field used to access the x-axis value from the items from the data
55841      * source.
55842      *
55843      * @property xField
55844      * @type String
55845      */
55846     xField: null,
55847
55848     /**
55849      * The field used to access the y-axis value from the items from the data
55850      * source.
55851      *
55852      * @property yField
55853      * @type String
55854      */
55855     yField: null,
55856
55857     /**
55858      * False to not show this series in the legend. Defaults to <tt>true</tt>.
55859      *
55860      * @property showInLegend
55861      * @type Boolean
55862      */
55863     showInLegend: true,
55864
55865     /**
55866      * Indicates which axis the series will bind to
55867      *
55868      * @property axis
55869      * @type String
55870      */
55871     axis: 'primary'
55872 });
55873
55874 /**
55875  * @class Ext.chart.ColumnSeries
55876  * @extends Ext.chart.CartesianSeries
55877  * ColumnSeries class for the charts widget.
55878  * @constructor
55879  */
55880 Ext.chart.ColumnSeries = Ext.extend(Ext.chart.CartesianSeries, {
55881     type: "column"
55882 });
55883
55884 /**
55885  * @class Ext.chart.LineSeries
55886  * @extends Ext.chart.CartesianSeries
55887  * LineSeries class for the charts widget.
55888  * @constructor
55889  */
55890 Ext.chart.LineSeries = Ext.extend(Ext.chart.CartesianSeries, {
55891     type: "line"
55892 });
55893
55894 /**
55895  * @class Ext.chart.BarSeries
55896  * @extends Ext.chart.CartesianSeries
55897  * BarSeries class for the charts widget.
55898  * @constructor
55899  */
55900 Ext.chart.BarSeries = Ext.extend(Ext.chart.CartesianSeries, {
55901     type: "bar"
55902 });
55903
55904
55905 /**
55906  * @class Ext.chart.PieSeries
55907  * @extends Ext.chart.Series
55908  * PieSeries class for the charts widget.
55909  * @constructor
55910  */
55911 Ext.chart.PieSeries = Ext.extend(Ext.chart.Series, {
55912     type: "pie",
55913     dataField: null,
55914     categoryField: null
55915 });/**
55916  * @class Ext.menu.Menu
55917  * @extends Ext.Container
55918  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class
55919  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>
55920  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>
55921  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}
55922  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line
55923  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}
55924  * for an example.</p>
55925  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with
55926  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>
55927  *
55928  * @xtype menu
55929  */
55930 Ext.menu.Menu = Ext.extend(Ext.Container, {
55931     /**
55932      * @cfg {Object} defaults
55933      * A config object that will be applied to all items added to this container either via the {@link #items}
55934      * config or via the {@link #add} method.  The defaults config can contain any number of
55935      * name/value property pairs to be added to each item, and should be valid for the types of items
55936      * being added to the menu.
55937      */
55938     /**
55939      * @cfg {Mixed} items
55940      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},
55941      * or general {@link Ext.Component Component}s.
55942      */
55943     /**
55944      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
55945      */
55946     minWidth : 120,
55947     /**
55948      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'
55949      * for bottom-right shadow (defaults to 'sides')
55950      */
55951     shadow : 'sides',
55952     /**
55953      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
55954      * this menu (defaults to 'tl-tr?')
55955      */
55956     subMenuAlign : 'tl-tr?',
55957     /**
55958      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
55959      * relative to its element of origin (defaults to 'tl-bl?')
55960      */
55961     defaultAlign : 'tl-bl?',
55962     /**
55963      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
55964      */
55965     allowOtherMenus : false,
55966     /**
55967      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
55968      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
55969      */
55970     ignoreParentClicks : false,
55971     /**
55972      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).
55973      */
55974     enableScrolling : true,
55975     /**
55976      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).
55977      */
55978     maxHeight : null,
55979     /**
55980      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).
55981      */
55982     scrollIncrement : 24,
55983     /**
55984      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
55985      */
55986     showSeparator : true,
55987     /**
55988      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to
55989      * change the default Menu popup position after aligning according to the {@link #defaultAlign}
55990      * configuration. Defaults to <tt>[0, 0]</tt>.
55991      */
55992     defaultOffsets : [0, 0],
55993
55994     /**
55995      * @cfg {Boolean} plain
55996      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.
55997      */
55998     plain : false,
55999
56000     /**
56001      * @cfg {Boolean} floating
56002      * <p>By default, a Menu configured as <b><code>floating:true</code></b>
56003      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,
56004      * floating Component with zindex=15000).
56005      * If configured as <b><code>floating:false</code></b>, the Menu may be
56006      * used as child item of another Container instead of a free-floating
56007      * {@link Ext.Layer Layer}.
56008      */
56009     floating : true,
56010
56011
56012     /**
56013      * @cfg {Number} zIndex
56014      * zIndex to use when the menu is floating.
56015      */
56016     zIndex: 15000,
56017
56018     // private
56019     hidden : true,
56020
56021     /**
56022      * @cfg {String/Object} layout
56023      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).
56024      * Developers <i>may</i> override this configuration option if another layout is required.
56025      * See {@link Ext.Container#layout} for additional information.
56026      */
56027     layout : 'menu',
56028     hideMode : 'offsets',    // Important for laying out Components
56029     scrollerHeight : 8,
56030     autoLayout : true,       // Provided for backwards compat
56031     defaultType : 'menuitem',
56032     bufferResize : false,
56033
56034     initComponent : function(){
56035         if(Ext.isArray(this.initialConfig)){
56036             Ext.apply(this, {items:this.initialConfig});
56037         }
56038         this.addEvents(
56039             /**
56040              * @event click
56041              * Fires when this menu is clicked (or when the enter key is pressed while it is active)
56042              * @param {Ext.menu.Menu} this
56043             * @param {Ext.menu.Item} menuItem The menu item that was clicked
56044              * @param {Ext.EventObject} e
56045              */
56046             'click',
56047             /**
56048              * @event mouseover
56049              * Fires when the mouse is hovering over this menu
56050              * @param {Ext.menu.Menu} this
56051              * @param {Ext.EventObject} e
56052              * @param {Ext.menu.Item} menuItem The menu item that was clicked
56053              */
56054             'mouseover',
56055             /**
56056              * @event mouseout
56057              * Fires when the mouse exits this menu
56058              * @param {Ext.menu.Menu} this
56059              * @param {Ext.EventObject} e
56060              * @param {Ext.menu.Item} menuItem The menu item that was clicked
56061              */
56062             'mouseout',
56063             /**
56064              * @event itemclick
56065              * Fires when a menu item contained in this menu is clicked
56066              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
56067              * @param {Ext.EventObject} e
56068              */
56069             'itemclick'
56070         );
56071         Ext.menu.MenuMgr.register(this);
56072         if(this.floating){
56073             Ext.EventManager.onWindowResize(this.hide, this);
56074         }else{
56075             if(this.initialConfig.hidden !== false){
56076                 this.hidden = false;
56077             }
56078             this.internalDefaults = {hideOnClick: false};
56079         }
56080         Ext.menu.Menu.superclass.initComponent.call(this);
56081         if(this.autoLayout){
56082             var fn = this.doLayout.createDelegate(this, []);
56083             this.on({
56084                 add: fn,
56085                 remove: fn
56086             });
56087         }
56088     },
56089
56090     //private
56091     getLayoutTarget : function() {
56092         return this.ul;
56093     },
56094
56095     // private
56096     onRender : function(ct, position){
56097         if(!ct){
56098             ct = Ext.getBody();
56099         }
56100
56101         var dh = {
56102             id: this.getId(),
56103             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),
56104             style: this.style,
56105             cn: [
56106                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},
56107                 {tag: 'ul', cls: 'x-menu-list'}
56108             ]
56109         };
56110         if(this.floating){
56111             this.el = new Ext.Layer({
56112                 shadow: this.shadow,
56113                 dh: dh,
56114                 constrain: false,
56115                 parentEl: ct,
56116                 zindex: this.zIndex
56117             });
56118         }else{
56119             this.el = ct.createChild(dh);
56120         }
56121         Ext.menu.Menu.superclass.onRender.call(this, ct, position);
56122
56123         if(!this.keyNav){
56124             this.keyNav = new Ext.menu.MenuNav(this);
56125         }
56126         // generic focus element
56127         this.focusEl = this.el.child('a.x-menu-focus');
56128         this.ul = this.el.child('ul.x-menu-list');
56129         this.mon(this.ul, {
56130             scope: this,
56131             click: this.onClick,
56132             mouseover: this.onMouseOver,
56133             mouseout: this.onMouseOut
56134         });
56135         if(this.enableScrolling){
56136             this.mon(this.el, {
56137                 scope: this,
56138                 delegate: '.x-menu-scroller',
56139                 click: this.onScroll,
56140                 mouseover: this.deactivateActive
56141             });
56142         }
56143     },
56144
56145     // private
56146     findTargetItem : function(e){
56147         var t = e.getTarget('.x-menu-list-item', this.ul, true);
56148         if(t && t.menuItemId){
56149             return this.items.get(t.menuItemId);
56150         }
56151     },
56152
56153     // private
56154     onClick : function(e){
56155         var t = this.findTargetItem(e);
56156         if(t){
56157             if(t.isFormField){
56158                 this.setActiveItem(t);
56159             }else if(t instanceof Ext.menu.BaseItem){
56160                 if(t.menu && this.ignoreParentClicks){
56161                     t.expandMenu();
56162                     e.preventDefault();
56163                 }else if(t.onClick){
56164                     t.onClick(e);
56165                     this.fireEvent('click', this, t, e);
56166                 }
56167             }
56168         }
56169     },
56170
56171     // private
56172     setActiveItem : function(item, autoExpand){
56173         if(item != this.activeItem){
56174             this.deactivateActive();
56175             if((this.activeItem = item).isFormField){
56176                 item.focus();
56177             }else{
56178                 item.activate(autoExpand);
56179             }
56180         }else if(autoExpand){
56181             item.expandMenu();
56182         }
56183     },
56184
56185     deactivateActive : function(){
56186         var a = this.activeItem;
56187         if(a){
56188             if(a.isFormField){
56189                 //Fields cannot deactivate, but Combos must collapse
56190                 if(a.collapse){
56191                     a.collapse();
56192                 }
56193             }else{
56194                 a.deactivate();
56195             }
56196             delete this.activeItem;
56197         }
56198     },
56199
56200     // private
56201     tryActivate : function(start, step){
56202         var items = this.items;
56203         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
56204             var item = items.get(i);
56205             if(!item.disabled && (item.canActivate || item.isFormField)){
56206                 this.setActiveItem(item, false);
56207                 return item;
56208             }
56209         }
56210         return false;
56211     },
56212
56213     // private
56214     onMouseOver : function(e){
56215         var t = this.findTargetItem(e);
56216         if(t){
56217             if(t.canActivate && !t.disabled){
56218                 this.setActiveItem(t, true);
56219             }
56220         }
56221         this.over = true;
56222         this.fireEvent('mouseover', this, e, t);
56223     },
56224
56225     // private
56226     onMouseOut : function(e){
56227         var t = this.findTargetItem(e);
56228         if(t){
56229             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){
56230                 this.activeItem.deactivate();
56231                 delete this.activeItem;
56232             }
56233         }
56234         this.over = false;
56235         this.fireEvent('mouseout', this, e, t);
56236     },
56237
56238     // private
56239     onScroll : function(e, t){
56240         if(e){
56241             e.stopEvent();
56242         }
56243         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
56244         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);
56245         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){
56246            this.onScrollerOut(null, t);
56247         }
56248     },
56249
56250     // private
56251     onScrollerIn : function(e, t){
56252         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
56253         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){
56254             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);
56255         }
56256     },
56257
56258     // private
56259     onScrollerOut : function(e, t){
56260         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);
56261     },
56262
56263     /**
56264      * If <code>{@link #floating}=true</code>, shows this menu relative to
56265      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.
56266      * @param {Mixed} element The element to align to
56267      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
56268      * the element (defaults to this.defaultAlign)
56269      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
56270      */
56271     show : function(el, pos, parentMenu){
56272         if(this.floating){
56273             this.parentMenu = parentMenu;
56274             if(!this.el){
56275                 this.render();
56276                 this.doLayout(false, true);
56277             }
56278             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);
56279         }else{
56280             Ext.menu.Menu.superclass.show.call(this);
56281         }
56282     },
56283
56284     /**
56285      * Displays this menu at a specific xy position and fires the 'show' event if a
56286      * handler for the 'beforeshow' event does not return false cancelling the operation.
56287      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
56288      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
56289      */
56290     showAt : function(xy, parentMenu){
56291         if(this.fireEvent('beforeshow', this) !== false){
56292             this.parentMenu = parentMenu;
56293             if(!this.el){
56294                 this.render();
56295             }
56296             if(this.enableScrolling){
56297                 // set the position so we can figure out the constrain value.
56298                 this.el.setXY(xy);
56299                 //constrain the value, keep the y coordinate the same
56300                 xy[1] = this.constrainScroll(xy[1]);
56301                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];
56302             }else{
56303                 //constrain to the viewport.
56304                 xy = this.el.adjustForConstraints(xy);
56305             }
56306             this.el.setXY(xy);
56307             this.el.show();
56308             Ext.menu.Menu.superclass.onShow.call(this);
56309             if(Ext.isIE){
56310                 // internal event, used so we don't couple the layout to the menu
56311                 this.fireEvent('autosize', this);
56312                 if(!Ext.isIE8){
56313                     this.el.repaint();
56314                 }
56315             }
56316             this.hidden = false;
56317             this.focus();
56318             this.fireEvent('show', this);
56319         }
56320     },
56321
56322     constrainScroll : function(y){
56323         var max, full = this.ul.setHeight('auto').getHeight(),
56324             returnY = y, normalY, parentEl, scrollTop, viewHeight;
56325         if(this.floating){
56326             parentEl = Ext.fly(this.el.dom.parentNode);
56327             scrollTop = parentEl.getScroll().top;
56328             viewHeight = parentEl.getViewSize().height;
56329             //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
56330             //of the view.
56331             normalY = y - scrollTop;
56332             max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
56333             if(full > viewHeight) {
56334                 max = viewHeight;
56335                 //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
56336                 returnY = y - normalY;
56337             } else if(max < full) {
56338                 returnY = y - (full - max);
56339                 max = full;
56340             }
56341         }else{
56342             max = this.getHeight();
56343         }
56344         // Always respect maxHeight 
56345         if (this.maxHeight){
56346             max = Math.min(this.maxHeight, max);
56347         }
56348         if(full > max && max > 0){
56349             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
56350             this.ul.setHeight(this.activeMax);
56351             this.createScrollers();
56352             this.el.select('.x-menu-scroller').setDisplayed('');
56353         }else{
56354             this.ul.setHeight(full);
56355             this.el.select('.x-menu-scroller').setDisplayed('none');
56356         }
56357         this.ul.dom.scrollTop = 0;
56358         return returnY;
56359     },
56360
56361     createScrollers : function(){
56362         if(!this.scroller){
56363             this.scroller = {
56364                 pos: 0,
56365                 top: this.el.insertFirst({
56366                     tag: 'div',
56367                     cls: 'x-menu-scroller x-menu-scroller-top',
56368                     html: '&#160;'
56369                 }),
56370                 bottom: this.el.createChild({
56371                     tag: 'div',
56372                     cls: 'x-menu-scroller x-menu-scroller-bottom',
56373                     html: '&#160;'
56374                 })
56375             };
56376             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);
56377             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {
56378                 listeners: {
56379                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)
56380                 }
56381             });
56382             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);
56383             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {
56384                 listeners: {
56385                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)
56386                 }
56387             });
56388         }
56389     },
56390
56391     onLayout : function(){
56392         if(this.isVisible()){
56393             if(this.enableScrolling){
56394                 this.constrainScroll(this.el.getTop());
56395             }
56396             if(this.floating){
56397                 this.el.sync();
56398             }
56399         }
56400     },
56401
56402     focus : function(){
56403         if(!this.hidden){
56404             this.doFocus.defer(50, this);
56405         }
56406     },
56407
56408     doFocus : function(){
56409         if(!this.hidden){
56410             this.focusEl.focus();
56411         }
56412     },
56413
56414     /**
56415      * Hides this menu and optionally all parent menus
56416      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
56417      */
56418     hide : function(deep){
56419         if (!this.isDestroyed) {
56420             this.deepHide = deep;
56421             Ext.menu.Menu.superclass.hide.call(this);
56422             delete this.deepHide;
56423         }
56424     },
56425
56426     // private
56427     onHide : function(){
56428         Ext.menu.Menu.superclass.onHide.call(this);
56429         this.deactivateActive();
56430         if(this.el && this.floating){
56431             this.el.hide();
56432         }
56433         var pm = this.parentMenu;
56434         if(this.deepHide === true && pm){
56435             if(pm.floating){
56436                 pm.hide(true);
56437             }else{
56438                 pm.deactivateActive();
56439             }
56440         }
56441     },
56442
56443     // private
56444     lookupComponent : function(c){
56445          if(Ext.isString(c)){
56446             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);
56447              this.applyDefaults(c);
56448          }else{
56449             if(Ext.isObject(c)){
56450                 c = this.getMenuItem(c);
56451             }else if(c.tagName || c.el){ // element. Wrap it.
56452                 c = new Ext.BoxComponent({
56453                     el: c
56454                 });
56455             }
56456          }
56457          return c;
56458     },
56459
56460     applyDefaults : function(c){
56461         if(!Ext.isString(c)){
56462             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);
56463             var d = this.internalDefaults;
56464             if(d){
56465                 if(c.events){
56466                     Ext.applyIf(c.initialConfig, d);
56467                     Ext.apply(c, d);
56468                 }else{
56469                     Ext.applyIf(c, d);
56470                 }
56471             }
56472         }
56473         return c;
56474     },
56475
56476     // private
56477     getMenuItem : function(config){
56478        if(!config.isXType){
56479             if(!config.xtype && Ext.isBoolean(config.checked)){
56480                 return new Ext.menu.CheckItem(config)
56481             }
56482             return Ext.create(config, this.defaultType);
56483         }
56484         return config;
56485     },
56486
56487     /**
56488      * Adds a separator bar to the menu
56489      * @return {Ext.menu.Item} The menu item that was added
56490      */
56491     addSeparator : function(){
56492         return this.add(new Ext.menu.Separator());
56493     },
56494
56495     /**
56496      * Adds an {@link Ext.Element} object to the menu
56497      * @param {Mixed} el The element or DOM node to add, or its id
56498      * @return {Ext.menu.Item} The menu item that was added
56499      */
56500     addElement : function(el){
56501         return this.add(new Ext.menu.BaseItem({
56502             el: el
56503         }));
56504     },
56505
56506     /**
56507      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
56508      * @param {Ext.menu.Item} item The menu item to add
56509      * @return {Ext.menu.Item} The menu item that was added
56510      */
56511     addItem : function(item){
56512         return this.add(item);
56513     },
56514
56515     /**
56516      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
56517      * @param {Object} config A MenuItem config object
56518      * @return {Ext.menu.Item} The menu item that was added
56519      */
56520     addMenuItem : function(config){
56521         return this.add(this.getMenuItem(config));
56522     },
56523
56524     /**
56525      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
56526      * @param {String} text The text to display in the menu item
56527      * @return {Ext.menu.Item} The menu item that was added
56528      */
56529     addText : function(text){
56530         return this.add(new Ext.menu.TextItem(text));
56531     },
56532
56533     //private
56534     onDestroy : function(){
56535         Ext.EventManager.removeResizeListener(this.hide, this);
56536         var pm = this.parentMenu;
56537         if(pm && pm.activeChild == this){
56538             delete pm.activeChild;
56539         }
56540         delete this.parentMenu;
56541         Ext.menu.Menu.superclass.onDestroy.call(this);
56542         Ext.menu.MenuMgr.unregister(this);
56543         if(this.keyNav) {
56544             this.keyNav.disable();
56545         }
56546         var s = this.scroller;
56547         if(s){
56548             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);
56549         }
56550         Ext.destroy(
56551             this.el,
56552             this.focusEl,
56553             this.ul
56554         );
56555     }
56556 });
56557
56558 Ext.reg('menu', Ext.menu.Menu);
56559
56560 // MenuNav is a private utility class used internally by the Menu
56561 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
56562     function up(e, m){
56563         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
56564             m.tryActivate(m.items.length-1, -1);
56565         }
56566     }
56567     function down(e, m){
56568         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
56569             m.tryActivate(0, 1);
56570         }
56571     }
56572     return {
56573         constructor : function(menu){
56574             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
56575             this.scope = this.menu = menu;
56576         },
56577
56578         doRelay : function(e, h){
56579             var k = e.getKey();
56580 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
56581             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
56582                 return false;
56583             }
56584             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
56585                 this.menu.tryActivate(0, 1);
56586                 return false;
56587             }
56588             return h.call(this.scope || this, e, this.menu);
56589         },
56590
56591         tab: function(e, m) {
56592             e.stopEvent();
56593             if (e.shiftKey) {
56594                 up(e, m);
56595             } else {
56596                 down(e, m);
56597             }
56598         },
56599
56600         up : up,
56601
56602         down : down,
56603
56604         right : function(e, m){
56605             if(m.activeItem){
56606                 m.activeItem.expandMenu(true);
56607             }
56608         },
56609
56610         left : function(e, m){
56611             m.hide();
56612             if(m.parentMenu && m.parentMenu.activeItem){
56613                 m.parentMenu.activeItem.activate();
56614             }
56615         },
56616
56617         enter : function(e, m){
56618             if(m.activeItem){
56619                 e.stopPropagation();
56620                 m.activeItem.onClick(e);
56621                 m.fireEvent('click', this, m.activeItem);
56622                 return true;
56623             }
56624         }
56625     };
56626 }());
56627 /**
56628  * @class Ext.menu.MenuMgr
56629  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
56630  * @singleton
56631  */
56632 Ext.menu.MenuMgr = function(){
56633    var menus, active, groups = {}, attached = false, lastShow = new Date();
56634
56635    // private - called when first menu is created
56636    function init(){
56637        menus = {};
56638        active = new Ext.util.MixedCollection();
56639        Ext.getDoc().addKeyListener(27, function(){
56640            if(active.length > 0){
56641                hideAll();
56642            }
56643        });
56644    }
56645
56646    // private
56647    function hideAll(){
56648        if(active && active.length > 0){
56649            var c = active.clone();
56650            c.each(function(m){
56651                m.hide();
56652            });
56653            return true;
56654        }
56655        return false;
56656    }
56657
56658    // private
56659    function onHide(m){
56660        active.remove(m);
56661        if(active.length < 1){
56662            Ext.getDoc().un("mousedown", onMouseDown);
56663            attached = false;
56664        }
56665    }
56666
56667    // private
56668    function onShow(m){
56669        var last = active.last();
56670        lastShow = new Date();
56671        active.add(m);
56672        if(!attached){
56673            Ext.getDoc().on("mousedown", onMouseDown);
56674            attached = true;
56675        }
56676        if(m.parentMenu){
56677           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
56678           m.parentMenu.activeChild = m;
56679        }else if(last && !last.isDestroyed && last.isVisible()){
56680           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
56681        }
56682    }
56683
56684    // private
56685    function onBeforeHide(m){
56686        if(m.activeChild){
56687            m.activeChild.hide();
56688        }
56689        if(m.autoHideTimer){
56690            clearTimeout(m.autoHideTimer);
56691            delete m.autoHideTimer;
56692        }
56693    }
56694
56695    // private
56696    function onBeforeShow(m){
56697        var pm = m.parentMenu;
56698        if(!pm && !m.allowOtherMenus){
56699            hideAll();
56700        }else if(pm && pm.activeChild){
56701            pm.activeChild.hide();
56702        }
56703    }
56704
56705    // private
56706    function onMouseDown(e){
56707        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
56708            hideAll();
56709        }
56710    }
56711
56712    // private
56713    function onBeforeCheck(mi, state){
56714        if(state){
56715            var g = groups[mi.group];
56716            for(var i = 0, l = g.length; i < l; i++){
56717                if(g[i] != mi){
56718                    g[i].setChecked(false);
56719                }
56720            }
56721        }
56722    }
56723
56724    return {
56725
56726        /**
56727         * Hides all menus that are currently visible
56728         * @return {Boolean} success True if any active menus were hidden.
56729         */
56730        hideAll : function(){
56731             return hideAll();
56732        },
56733
56734        // private
56735        register : function(menu){
56736            if(!menus){
56737                init();
56738            }
56739            menus[menu.id] = menu;
56740            menu.on({
56741                beforehide: onBeforeHide,
56742                hide: onHide,
56743                beforeshow: onBeforeShow,
56744                show: onShow
56745            });
56746        },
56747
56748         /**
56749          * Returns a {@link Ext.menu.Menu} object
56750          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
56751          * be used to generate and return a new Menu instance.
56752          * @return {Ext.menu.Menu} The specified menu, or null if none are found
56753          */
56754        get : function(menu){
56755            if(typeof menu == "string"){ // menu id
56756                if(!menus){  // not initialized, no menus to return
56757                    return null;
56758                }
56759                return menus[menu];
56760            }else if(menu.events){  // menu instance
56761                return menu;
56762            }else if(typeof menu.length == 'number'){ // array of menu items?
56763                return new Ext.menu.Menu({items:menu});
56764            }else{ // otherwise, must be a config
56765                return Ext.create(menu, 'menu');
56766            }
56767        },
56768
56769        // private
56770        unregister : function(menu){
56771            delete menus[menu.id];
56772            menu.un("beforehide", onBeforeHide);
56773            menu.un("hide", onHide);
56774            menu.un("beforeshow", onBeforeShow);
56775            menu.un("show", onShow);
56776        },
56777
56778        // private
56779        registerCheckable : function(menuItem){
56780            var g = menuItem.group;
56781            if(g){
56782                if(!groups[g]){
56783                    groups[g] = [];
56784                }
56785                groups[g].push(menuItem);
56786                menuItem.on("beforecheckchange", onBeforeCheck);
56787            }
56788        },
56789
56790        // private
56791        unregisterCheckable : function(menuItem){
56792            var g = menuItem.group;
56793            if(g){
56794                groups[g].remove(menuItem);
56795                menuItem.un("beforecheckchange", onBeforeCheck);
56796            }
56797        },
56798
56799        getCheckedItem : function(groupId){
56800            var g = groups[groupId];
56801            if(g){
56802                for(var i = 0, l = g.length; i < l; i++){
56803                    if(g[i].checked){
56804                        return g[i];
56805                    }
56806                }
56807            }
56808            return null;
56809        },
56810
56811        setCheckedItem : function(groupId, itemId){
56812            var g = groups[groupId];
56813            if(g){
56814                for(var i = 0, l = g.length; i < l; i++){
56815                    if(g[i].id == itemId){
56816                        g[i].setChecked(true);
56817                    }
56818                }
56819            }
56820            return null;
56821        }
56822    };
56823 }();
56824 /**
56825  * @class Ext.menu.BaseItem
56826  * @extends Ext.Component
56827  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
56828  * management and base configuration options shared by all menu components.
56829  * @constructor
56830  * Creates a new BaseItem
56831  * @param {Object} config Configuration options
56832  * @xtype menubaseitem
56833  */
56834 Ext.menu.BaseItem = Ext.extend(Ext.Component, {
56835     /**
56836      * @property parentMenu
56837      * @type Ext.menu.Menu
56838      * The parent Menu of this Item.
56839      */
56840     /**
56841      * @cfg {Function} handler
56842      * A function that will handle the click event of this menu item (optional).
56843      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
56844      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
56845      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
56846      * </ul></div>
56847      */
56848     /**
56849      * @cfg {Object} scope
56850      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
56851      */
56852     /**
56853      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
56854      */
56855     canActivate : false,
56856     /**
56857      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
56858      */
56859     activeClass : "x-menu-item-active",
56860     /**
56861      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
56862      */
56863     hideOnClick : true,
56864     /**
56865      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 1)
56866      */
56867     clickHideDelay : 1,
56868
56869     // private
56870     ctype : "Ext.menu.BaseItem",
56871
56872     // private
56873     actionMode : "container",
56874
56875     initComponent : function(){
56876         Ext.menu.BaseItem.superclass.initComponent.call(this);
56877         this.addEvents(
56878             /**
56879              * @event click
56880              * Fires when this item is clicked
56881              * @param {Ext.menu.BaseItem} this
56882              * @param {Ext.EventObject} e
56883              */
56884             'click',
56885             /**
56886              * @event activate
56887              * Fires when this item is activated
56888              * @param {Ext.menu.BaseItem} this
56889              */
56890             'activate',
56891             /**
56892              * @event deactivate
56893              * Fires when this item is deactivated
56894              * @param {Ext.menu.BaseItem} this
56895              */
56896             'deactivate'
56897         );
56898         if(this.handler){
56899             this.on("click", this.handler, this.scope);
56900         }
56901     },
56902
56903     // private
56904     onRender : function(container, position){
56905         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
56906         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
56907             this.parentMenu = this.ownerCt;
56908         }else{
56909             this.container.addClass('x-menu-list-item');
56910             this.mon(this.el, {
56911                 scope: this,
56912                 click: this.onClick,
56913                 mouseenter: this.activate,
56914                 mouseleave: this.deactivate
56915             });
56916         }
56917     },
56918
56919     /**
56920      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
56921      * config property).  If an existing handler is already registered, it will be unregistered for you.
56922      * @param {Function} handler The function that should be called on click
56923      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function is executed. Defaults to this menu item.
56924      */
56925     setHandler : function(handler, scope){
56926         if(this.handler){
56927             this.un("click", this.handler, this.scope);
56928         }
56929         this.on("click", this.handler = handler, this.scope = scope);
56930     },
56931
56932     // private
56933     onClick : function(e){
56934         if(!this.disabled && this.fireEvent("click", this, e) !== false
56935                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
56936             this.handleClick(e);
56937         }else{
56938             e.stopEvent();
56939         }
56940     },
56941
56942     // private
56943     activate : function(){
56944         if(this.disabled){
56945             return false;
56946         }
56947         var li = this.container;
56948         li.addClass(this.activeClass);
56949         this.region = li.getRegion().adjust(2, 2, -2, -2);
56950         this.fireEvent("activate", this);
56951         return true;
56952     },
56953
56954     // private
56955     deactivate : function(){
56956         this.container.removeClass(this.activeClass);
56957         this.fireEvent("deactivate", this);
56958     },
56959
56960     // private
56961     shouldDeactivate : function(e){
56962         return !this.region || !this.region.contains(e.getPoint());
56963     },
56964
56965     // private
56966     handleClick : function(e){
56967         var pm = this.parentMenu;
56968         if(this.hideOnClick){
56969             if(pm.floating){
56970                 pm.hide.defer(this.clickHideDelay, pm, [true]);
56971             }else{
56972                 pm.deactivateActive();
56973             }
56974         }
56975     },
56976
56977     // private. Do nothing
56978     expandMenu : Ext.emptyFn,
56979
56980     // private. Do nothing
56981     hideMenu : Ext.emptyFn
56982 });
56983 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
56984  * @class Ext.menu.TextItem
56985  * @extends Ext.menu.BaseItem
56986  * Adds a static text string to a menu, usually used as either a heading or group separator.
56987  * @constructor
56988  * Creates a new TextItem
56989  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
56990  * is applied as a config object (and should contain a <tt>text</tt> property).
56991  * @xtype menutextitem
56992  */
56993 Ext.menu.TextItem = Ext.extend(Ext.menu.BaseItem, {
56994     /**
56995      * @cfg {String} text The text to display for this item (defaults to '')
56996      */
56997     /**
56998      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
56999      */
57000     hideOnClick : false,
57001     /**
57002      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
57003      */
57004     itemCls : "x-menu-text",
57005     
57006     constructor : function(config){
57007         if(typeof config == 'string'){
57008             config = {text: config}
57009         }
57010         Ext.menu.TextItem.superclass.constructor.call(this, config);
57011     },
57012
57013     // private
57014     onRender : function(){
57015         var s = document.createElement("span");
57016         s.className = this.itemCls;
57017         s.innerHTML = this.text;
57018         this.el = s;
57019         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
57020     }
57021 });
57022 Ext.reg('menutextitem', Ext.menu.TextItem);/**
57023  * @class Ext.menu.Separator
57024  * @extends Ext.menu.BaseItem
57025  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
57026  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
57027  * @constructor
57028  * @param {Object} config Configuration options
57029  * @xtype menuseparator
57030  */
57031 Ext.menu.Separator = Ext.extend(Ext.menu.BaseItem, {
57032     /**
57033      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
57034      */
57035     itemCls : "x-menu-sep",
57036     /**
57037      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
57038      */
57039     hideOnClick : false,
57040     
57041     /** 
57042      * @cfg {String} activeClass
57043      * @hide 
57044      */
57045     activeClass: '',
57046
57047     // private
57048     onRender : function(li){
57049         var s = document.createElement("span");
57050         s.className = this.itemCls;
57051         s.innerHTML = "&#160;";
57052         this.el = s;
57053         li.addClass("x-menu-sep-li");
57054         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
57055     }
57056 });
57057 Ext.reg('menuseparator', Ext.menu.Separator);/**
57058  * @class Ext.menu.Item
57059  * @extends Ext.menu.BaseItem
57060  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
57061  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific
57062  * activation and click handling.
57063  * @constructor
57064  * Creates a new Item
57065  * @param {Object} config Configuration options
57066  * @xtype menuitem
57067  */
57068 Ext.menu.Item = Ext.extend(Ext.menu.BaseItem, {
57069     /**
57070      * @property menu
57071      * @type Ext.menu.Menu
57072      * The submenu associated with this Item if one was configured.
57073      */
57074     /**
57075      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an
57076      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.
57077      */
57078     /**
57079      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If
57080      * icon is specified {@link #iconCls} should not be.
57081      */
57082     /**
57083      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for
57084      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.
57085      */
57086     /**
57087      * @cfg {String} text The text to display in this item (defaults to '').
57088      */
57089     /**
57090      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').
57091      */
57092     /**
57093      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').
57094      */
57095     /**
57096      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')
57097      */
57098     itemCls : 'x-menu-item',
57099     /**
57100      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
57101      */
57102     canActivate : true,
57103     /**
57104      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
57105      */
57106     showDelay: 200,
57107     // doc'd in BaseItem
57108     hideDelay: 200,
57109
57110     // private
57111     ctype: 'Ext.menu.Item',
57112
57113     initComponent : function(){
57114         Ext.menu.Item.superclass.initComponent.call(this);
57115         if(this.menu){
57116             this.menu = Ext.menu.MenuMgr.get(this.menu);
57117             this.menu.ownerCt = this;
57118         }
57119     },
57120
57121     // private
57122     onRender : function(container, position){
57123         if (!this.itemTpl) {
57124             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(
57125                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',
57126                     '<tpl if="hrefTarget">',
57127                         ' target="{hrefTarget}"',
57128                     '</tpl>',
57129                  '>',
57130                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',
57131                      '<span class="x-menu-item-text">{text}</span>',
57132                  '</a>'
57133              );
57134         }
57135         var a = this.getTemplateArgs();
57136         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);
57137         this.iconEl = this.el.child('img.x-menu-item-icon');
57138         this.textEl = this.el.child('.x-menu-item-text');
57139         if(!this.href) { // if no link defined, prevent the default anchor event
57140             this.mon(this.el, 'click', Ext.emptyFn, null, { preventDefault: true });
57141         }
57142         Ext.menu.Item.superclass.onRender.call(this, container, position);
57143     },
57144
57145     getTemplateArgs: function() {
57146         return {
57147             id: this.id,
57148             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),
57149             href: this.href || '#',
57150             hrefTarget: this.hrefTarget,
57151             icon: this.icon || Ext.BLANK_IMAGE_URL,
57152             iconCls: this.iconCls || '',
57153             text: this.itemText||this.text||'&#160;'
57154         };
57155     },
57156
57157     /**
57158      * Sets the text to display in this menu item
57159      * @param {String} text The text to display
57160      */
57161     setText : function(text){
57162         this.text = text||'&#160;';
57163         if(this.rendered){
57164             this.textEl.update(this.text);
57165             this.parentMenu.layout.doAutoSize();
57166         }
57167     },
57168
57169     /**
57170      * Sets the CSS class to apply to the item's icon element
57171      * @param {String} cls The CSS class to apply
57172      */
57173     setIconClass : function(cls){
57174         var oldCls = this.iconCls;
57175         this.iconCls = cls;
57176         if(this.rendered){
57177             this.iconEl.replaceClass(oldCls, this.iconCls);
57178         }
57179     },
57180
57181     //private
57182     beforeDestroy: function(){
57183         if (this.menu){
57184             delete this.menu.ownerCt;
57185             this.menu.destroy();
57186         }
57187         Ext.menu.Item.superclass.beforeDestroy.call(this);
57188     },
57189
57190     // private
57191     handleClick : function(e){
57192         if(!this.href){ // if no link defined, stop the event automatically
57193             e.stopEvent();
57194         }
57195         Ext.menu.Item.superclass.handleClick.apply(this, arguments);
57196     },
57197
57198     // private
57199     activate : function(autoExpand){
57200         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){
57201             this.focus();
57202             if(autoExpand){
57203                 this.expandMenu();
57204             }
57205         }
57206         return true;
57207     },
57208
57209     // private
57210     shouldDeactivate : function(e){
57211         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){
57212             if(this.menu && this.menu.isVisible()){
57213                 return !this.menu.getEl().getRegion().contains(e.getPoint());
57214             }
57215             return true;
57216         }
57217         return false;
57218     },
57219
57220     // private
57221     deactivate : function(){
57222         Ext.menu.Item.superclass.deactivate.apply(this, arguments);
57223         this.hideMenu();
57224     },
57225
57226     // private
57227     expandMenu : function(autoActivate){
57228         if(!this.disabled && this.menu){
57229             clearTimeout(this.hideTimer);
57230             delete this.hideTimer;
57231             if(!this.menu.isVisible() && !this.showTimer){
57232                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
57233             }else if (this.menu.isVisible() && autoActivate){
57234                 this.menu.tryActivate(0, 1);
57235             }
57236         }
57237     },
57238
57239     // private
57240     deferExpand : function(autoActivate){
57241         delete this.showTimer;
57242         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);
57243         if(autoActivate){
57244             this.menu.tryActivate(0, 1);
57245         }
57246     },
57247
57248     // private
57249     hideMenu : function(){
57250         clearTimeout(this.showTimer);
57251         delete this.showTimer;
57252         if(!this.hideTimer && this.menu && this.menu.isVisible()){
57253             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
57254         }
57255     },
57256
57257     // private
57258     deferHide : function(){
57259         delete this.hideTimer;
57260         if(this.menu.over){
57261             this.parentMenu.setActiveItem(this, false);
57262         }else{
57263             this.menu.hide();
57264         }
57265     }
57266 });
57267 Ext.reg('menuitem', Ext.menu.Item);/**
57268  * @class Ext.menu.CheckItem
57269  * @extends Ext.menu.Item
57270  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
57271  * @constructor
57272  * Creates a new CheckItem
57273  * @param {Object} config Configuration options
57274  * @xtype menucheckitem
57275  */
57276 Ext.menu.CheckItem = Ext.extend(Ext.menu.Item, {
57277     /**
57278      * @cfg {String} group
57279      * All check items with the same group name will automatically be grouped into a single-select
57280      * radio button group (defaults to '')
57281      */
57282     /**
57283      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
57284      */
57285     itemCls : "x-menu-item x-menu-check-item",
57286     /**
57287      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
57288      */
57289     groupClass : "x-menu-group-item",
57290
57291     /**
57292      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
57293      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
57294      * initialized with checked = true will be rendered as checked.
57295      */
57296     checked: false,
57297
57298     // private
57299     ctype: "Ext.menu.CheckItem",
57300     
57301     initComponent : function(){
57302         Ext.menu.CheckItem.superclass.initComponent.call(this);
57303             this.addEvents(
57304                 /**
57305                  * @event beforecheckchange
57306                  * Fires before the checked value is set, providing an opportunity to cancel if needed
57307                  * @param {Ext.menu.CheckItem} this
57308                  * @param {Boolean} checked The new checked value that will be set
57309                  */
57310                 "beforecheckchange" ,
57311                 /**
57312                  * @event checkchange
57313                  * Fires after the checked value has been set
57314                  * @param {Ext.menu.CheckItem} this
57315                  * @param {Boolean} checked The checked value that was set
57316                  */
57317                 "checkchange"
57318             );
57319             /**
57320              * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
57321              * is provided, it will be called automatically when the checkchange event fires.
57322              * @param {Ext.menu.CheckItem} this
57323              * @param {Boolean} checked The checked value that was set
57324              * @method checkHandler
57325              */
57326             if(this.checkHandler){
57327                 this.on('checkchange', this.checkHandler, this.scope);
57328             }
57329             Ext.menu.MenuMgr.registerCheckable(this);
57330     },
57331
57332     // private
57333     onRender : function(c){
57334         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
57335         if(this.group){
57336             this.el.addClass(this.groupClass);
57337         }
57338         if(this.checked){
57339             this.checked = false;
57340             this.setChecked(true, true);
57341         }
57342     },
57343
57344     // private
57345     destroy : function(){
57346         Ext.menu.MenuMgr.unregisterCheckable(this);
57347         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
57348     },
57349
57350     /**
57351      * Set the checked state of this item
57352      * @param {Boolean} checked The new checked value
57353      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
57354      */
57355     setChecked : function(state, suppressEvent){
57356         var suppress = suppressEvent === true;
57357         if(this.checked != state && (suppress || this.fireEvent("beforecheckchange", this, state) !== false)){
57358             if(this.container){
57359                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
57360             }
57361             this.checked = state;
57362             if(!suppress){
57363                 this.fireEvent("checkchange", this, state);
57364             }
57365         }
57366     },
57367
57368     // private
57369     handleClick : function(e){
57370        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
57371            this.setChecked(!this.checked);
57372        }
57373        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
57374     }
57375 });
57376 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**
57377  * @class Ext.menu.DateMenu
57378  * @extends Ext.menu.Menu
57379  * <p>A menu containing an {@link Ext.DatePicker} Component.</p>
57380  * <p>Notes:</p><div class="mdetail-params"><ul>
57381  * <li>Although not listed here, the <b>constructor</b> for this class
57382  * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>
57383  * <li>If subclassing DateMenu, any configuration options for the DatePicker must be
57384  * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.
57385  * Applying {@link Ext.DatePicker DatePicker} configuration settings to
57386  * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>
57387  * </ul></div>
57388  * @xtype datemenu
57389  */
57390  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {
57391     /** 
57392      * @cfg {Boolean} enableScrolling
57393      * @hide 
57394      */
57395     enableScrolling : false,
57396     /**
57397      * @cfg {Function} handler
57398      * Optional. A function that will handle the select event of this menu.
57399      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
57400      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>
57401      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
57402      * </ul></div>
57403      */
57404     /**
57405      * @cfg {Object} scope
57406      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
57407      * function will be called.  Defaults to this DateMenu instance.
57408      */    
57409     /** 
57410      * @cfg {Boolean} hideOnClick
57411      * False to continue showing the menu after a date is selected, defaults to true.
57412      */
57413     hideOnClick : true,
57414     
57415     /** 
57416      * @cfg {String} pickerId
57417      * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.
57418      */
57419     pickerId : null,
57420     
57421     /** 
57422      * @cfg {Number} maxHeight
57423      * @hide 
57424      */
57425     /** 
57426      * @cfg {Number} scrollIncrement
57427      * @hide 
57428      */
57429     /**
57430      * The {@link Ext.DatePicker} instance for this DateMenu
57431      * @property picker
57432      * @type DatePicker
57433      */
57434     cls : 'x-date-menu',
57435     
57436     /**
57437      * @event click
57438      * @hide
57439      */
57440     
57441     /**
57442      * @event itemclick
57443      * @hide
57444      */
57445
57446     initComponent : function(){
57447         this.on('beforeshow', this.onBeforeShow, this);
57448         if(this.strict = (Ext.isIE7 && Ext.isStrict)){
57449             this.on('show', this.onShow, this, {single: true, delay: 20});
57450         }
57451         Ext.apply(this, {
57452             plain: true,
57453             showSeparator: false,
57454             items: this.picker = new Ext.DatePicker(Ext.applyIf({
57455                 internalRender: this.strict || !Ext.isIE,
57456                 ctCls: 'x-menu-date-item',
57457                 id: this.pickerId
57458             }, this.initialConfig))
57459         });
57460         this.picker.purgeListeners();
57461         Ext.menu.DateMenu.superclass.initComponent.call(this);
57462         /**
57463          * @event select
57464          * Fires when a date is selected from the {@link #picker Ext.DatePicker}
57465          * @param {DatePicker} picker The {@link #picker Ext.DatePicker}
57466          * @param {Date} date The selected date
57467          */
57468         this.relayEvents(this.picker, ['select']);
57469         this.on('show', this.picker.focus, this.picker);
57470         this.on('select', this.menuHide, this);
57471         if(this.handler){
57472             this.on('select', this.handler, this.scope || this);
57473         }
57474     },
57475
57476     menuHide : function() {
57477         if(this.hideOnClick){
57478             this.hide(true);
57479         }
57480     },
57481
57482     onBeforeShow : function(){
57483         if(this.picker){
57484             this.picker.hideMonthPicker(true);
57485         }
57486     },
57487
57488     onShow : function(){
57489         var el = this.picker.getEl();
57490         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode
57491     }
57492  });
57493  Ext.reg('datemenu', Ext.menu.DateMenu);
57494  /**
57495  * @class Ext.menu.ColorMenu
57496  * @extends Ext.menu.Menu
57497  * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>
57498  * <p>Notes:</p><div class="mdetail-params"><ul>
57499  * <li>Although not listed here, the <b>constructor</b> for this class
57500  * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>
57501  * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be
57502  * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.
57503  * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to
57504  * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>
57505  * </ul></div> * 
57506  * @xtype colormenu
57507  */
57508  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {
57509     /** 
57510      * @cfg {Boolean} enableScrolling
57511      * @hide 
57512      */
57513     enableScrolling : false,
57514     /**
57515      * @cfg {Function} handler
57516      * Optional. A function that will handle the select event of this menu.
57517      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
57518      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
57519      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
57520      * </ul></div>
57521      */
57522     /**
57523      * @cfg {Object} scope
57524      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
57525      * function will be called.  Defaults to this ColorMenu instance.
57526      */    
57527     
57528     /** 
57529      * @cfg {Boolean} hideOnClick
57530      * False to continue showing the menu after a color is selected, defaults to true.
57531      */
57532     hideOnClick : true,
57533     
57534     cls : 'x-color-menu',
57535     
57536     /** 
57537      * @cfg {String} paletteId
57538      * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.
57539      */
57540     paletteId : null,
57541     
57542     /** 
57543      * @cfg {Number} maxHeight
57544      * @hide 
57545      */
57546     /** 
57547      * @cfg {Number} scrollIncrement
57548      * @hide 
57549      */
57550     /**
57551      * @property palette
57552      * @type ColorPalette
57553      * The {@link Ext.ColorPalette} instance for this ColorMenu
57554      */
57555     
57556     
57557     /**
57558      * @event click
57559      * @hide
57560      */
57561     
57562     /**
57563      * @event itemclick
57564      * @hide
57565      */
57566     
57567     initComponent : function(){
57568         Ext.apply(this, {
57569             plain: true,
57570             showSeparator: false,
57571             items: this.palette = new Ext.ColorPalette(Ext.applyIf({
57572                 id: this.paletteId
57573             }, this.initialConfig))
57574         });
57575         this.palette.purgeListeners();
57576         Ext.menu.ColorMenu.superclass.initComponent.call(this);
57577         /**
57578          * @event select
57579          * Fires when a color is selected from the {@link #palette Ext.ColorPalette}
57580          * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}
57581              * @param {String} color The 6-digit color hex code (without the # symbol)
57582          */
57583         this.relayEvents(this.palette, ['select']);
57584         this.on('select', this.menuHide, this);
57585         if(this.handler){
57586             this.on('select', this.handler, this.scope || this);
57587         }
57588     },
57589
57590     menuHide : function(){
57591         if(this.hideOnClick){
57592             this.hide(true);
57593         }
57594     }
57595 });
57596 Ext.reg('colormenu', Ext.menu.ColorMenu);
57597 /**
57598  * @class Ext.form.Field
57599  * @extends Ext.BoxComponent
57600  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
57601  * @constructor
57602  * Creates a new Field
57603  * @param {Object} config Configuration options
57604  * @xtype field
57605  */
57606 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
57607     /**
57608      * <p>The label Element associated with this Field. <b>Only available after this Field has been rendered by a
57609      * {@link form Ext.layout.FormLayout} layout manager.</b></p>
57610      * @type Ext.Element
57611      * @property label
57612      */
57613     /**
57614      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
57615      * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
57616      * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
57617      * is not supported and should be avoided.
57618      */
57619     /**
57620      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
57621      * not those which are built via applyTo (defaults to undefined).
57622      */
57623     /**
57624      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
57625      */
57626     /**
57627      * @cfg {String} name The field's HTML name attribute (defaults to '').
57628      * <b>Note</b>: this property must be set if this field is to be automatically included with
57629      * {@link Ext.form.BasicForm#submit form submit()}.
57630      */
57631     /**
57632      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
57633      */
57634
57635     /**
57636      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
57637      */
57638     invalidClass : 'x-form-invalid',
57639     /**
57640      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
57641      * (defaults to 'The value in this field is invalid')
57642      */
57643     invalidText : 'The value in this field is invalid',
57644     /**
57645      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
57646      */
57647     focusClass : 'x-form-focus',
57648     /**
57649      * @cfg {Boolean} preventMark
57650      * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
57651      * Defaults to <tt>false</tt>.
57652      */
57653     /**
57654      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
57655       automatic validation (defaults to 'keyup').
57656      */
57657     validationEvent : 'keyup',
57658     /**
57659      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
57660      */
57661     validateOnBlur : true,
57662     /**
57663      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
57664      * is initiated (defaults to 250)
57665      */
57666     validationDelay : 250,
57667     /**
57668      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
57669      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
57670      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
57671      * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
57672      */
57673     defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
57674     /**
57675      * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
57676      */
57677     fieldClass : 'x-form-field',
57678     /**
57679      * @cfg {String} msgTarget <p>The location where the message text set through {@link #markInvalid} should display.
57680      * Must be one of the following values:</p>
57681      * <div class="mdetail-params"><ul>
57682      * <li><code>qtip</code> Display a quick tip containing the message when the user hovers over the field. This is the default.
57683      * <div class="subdesc"><b>{@link Ext.QuickTips#init Ext.QuickTips.init} must have been called for this setting to work.</b></div</li>
57684      * <li><code>title</code> Display the message in a default browser title attribute popup.</li>
57685      * <li><code>under</code> Add a block div beneath the field containing the error message.</li>
57686      * <li><code>side</code> Add an error icon to the right of the field, displaying the message in a popup on hover.</li>
57687      * <li><code>[element id]</code> Add the error message directly to the innerHTML of the specified element.</li>
57688      * </ul></div>
57689      */
57690     msgTarget : 'qtip',
57691     /**
57692      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
57693      * (defaults to 'normal').
57694      */
57695     msgFx : 'normal',
57696     /**
57697      * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
57698      * (defaults to <tt>false</tt>).
57699      * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
57700      * Setting <code>readOnly=true</code>, for example, will not disable triggering a
57701      * ComboBox or DateField; it gives you the option of forcing the user to choose
57702      * via the trigger without typing in the text box. To hide the trigger use
57703      * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
57704      */
57705     readOnly : false,
57706     /**
57707      * @cfg {Boolean} disabled True to disable the field (defaults to false).
57708      * <p>Be aware that conformant with the <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1">HTML specification</a>,
57709      * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
57710      */
57711     disabled : false,
57712     /**
57713      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
57714      * Defaults to <tt>true</tt>.
57715      */
57716     submitValue: true,
57717
57718     // private
57719     isFormField : true,
57720
57721     // private
57722     msgDisplay: '',
57723
57724     // private
57725     hasFocus : false,
57726
57727     // private
57728     initComponent : function(){
57729         Ext.form.Field.superclass.initComponent.call(this);
57730         this.addEvents(
57731             /**
57732              * @event focus
57733              * Fires when this field receives input focus.
57734              * @param {Ext.form.Field} this
57735              */
57736             'focus',
57737             /**
57738              * @event blur
57739              * Fires when this field loses input focus.
57740              * @param {Ext.form.Field} this
57741              */
57742             'blur',
57743             /**
57744              * @event specialkey
57745              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
57746              * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
57747              * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
57748              * For example: <pre><code>
57749 var form = new Ext.form.FormPanel({
57750     ...
57751     items: [{
57752             fieldLabel: 'Field 1',
57753             name: 'field1',
57754             allowBlank: false
57755         },{
57756             fieldLabel: 'Field 2',
57757             name: 'field2',
57758             listeners: {
57759                 specialkey: function(field, e){
57760                     // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
57761                     // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
57762                     if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
57763                         var form = field.ownerCt.getForm();
57764                         form.submit();
57765                     }
57766                 }
57767             }
57768         }
57769     ],
57770     ...
57771 });
57772              * </code></pre>
57773              * @param {Ext.form.Field} this
57774              * @param {Ext.EventObject} e The event object
57775              */
57776             'specialkey',
57777             /**
57778              * @event change
57779              * Fires just before the field blurs if the field value has changed.
57780              * @param {Ext.form.Field} this
57781              * @param {Mixed} newValue The new value
57782              * @param {Mixed} oldValue The original value
57783              */
57784             'change',
57785             /**
57786              * @event invalid
57787              * Fires after the field has been marked as invalid.
57788              * @param {Ext.form.Field} this
57789              * @param {String} msg The validation message
57790              */
57791             'invalid',
57792             /**
57793              * @event valid
57794              * Fires after the field has been validated with no errors.
57795              * @param {Ext.form.Field} this
57796              */
57797             'valid'
57798         );
57799     },
57800
57801     /**
57802      * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
57803      * attribute of the field if available.
57804      * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
57805      */
57806     getName : function(){
57807         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
57808     },
57809
57810     // private
57811     onRender : function(ct, position){
57812         if(!this.el){
57813             var cfg = this.getAutoCreate();
57814
57815             if(!cfg.name){
57816                 cfg.name = this.name || this.id;
57817             }
57818             if(this.inputType){
57819                 cfg.type = this.inputType;
57820             }
57821             this.autoEl = cfg;
57822         }
57823         Ext.form.Field.superclass.onRender.call(this, ct, position);
57824         if(this.submitValue === false){
57825             this.el.dom.removeAttribute('name');
57826         }
57827         var type = this.el.dom.type;
57828         if(type){
57829             if(type == 'password'){
57830                 type = 'text';
57831             }
57832             this.el.addClass('x-form-'+type);
57833         }
57834         if(this.readOnly){
57835             this.setReadOnly(true);
57836         }
57837         if(this.tabIndex !== undefined){
57838             this.el.dom.setAttribute('tabIndex', this.tabIndex);
57839         }
57840
57841         this.el.addClass([this.fieldClass, this.cls]);
57842     },
57843
57844     // private
57845     getItemCt : function(){
57846         return this.itemCt;
57847     },
57848
57849     // private
57850     initValue : function(){
57851         if(this.value !== undefined){
57852             this.setValue(this.value);
57853         }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
57854             this.setValue(this.el.dom.value);
57855         }
57856         /**
57857          * The original value of the field as configured in the {@link #value} configuration, or
57858          * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
57859          * setting is <code>true</code>.
57860          * @type mixed
57861          * @property originalValue
57862          */
57863         this.originalValue = this.getValue();
57864     },
57865
57866     /**
57867      * <p>Returns true if the value of this Field has been changed from its original value.
57868      * Will return false if the field is disabled or has not been rendered yet.</p>
57869      * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
57870      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
57871      * then the <i>original value</i> is updated when the values are loaded by
57872      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
57873      * @return {Boolean} True if this field has been changed from its original value (and
57874      * is not disabled), false otherwise.
57875      */
57876     isDirty : function() {
57877         if(this.disabled || !this.rendered) {
57878             return false;
57879         }
57880         return String(this.getValue()) !== String(this.originalValue);
57881     },
57882
57883     /**
57884      * Sets the read only state of this field.
57885      * @param {Boolean} readOnly Whether the field should be read only.
57886      */
57887     setReadOnly : function(readOnly){
57888         if(this.rendered){
57889             this.el.dom.readOnly = readOnly;
57890         }
57891         this.readOnly = readOnly;
57892     },
57893
57894     // private
57895     afterRender : function(){
57896         Ext.form.Field.superclass.afterRender.call(this);
57897         this.initEvents();
57898         this.initValue();
57899     },
57900
57901     // private
57902     fireKey : function(e){
57903         if(e.isSpecialKey()){
57904             this.fireEvent('specialkey', this, e);
57905         }
57906     },
57907
57908     /**
57909      * Resets the current field value to the originally loaded value and clears any validation messages.
57910      * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
57911      */
57912     reset : function(){
57913         this.setValue(this.originalValue);
57914         this.clearInvalid();
57915     },
57916
57917     // private
57918     initEvents : function(){
57919         this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey,  this);
57920         this.mon(this.el, 'focus', this.onFocus, this);
57921
57922         // standardise buffer across all browsers + OS-es for consistent event order.
57923         // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
57924         this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
57925     },
57926
57927     // private
57928     preFocus: Ext.emptyFn,
57929
57930     // private
57931     onFocus : function(){
57932         this.preFocus();
57933         if(this.focusClass){
57934             this.el.addClass(this.focusClass);
57935         }
57936         if(!this.hasFocus){
57937             this.hasFocus = true;
57938             /**
57939              * <p>The value that the Field had at the time it was last focused. This is the value that is passed
57940              * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.</p>
57941              * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
57942              * @type mixed
57943              * @property startValue
57944              */
57945             this.startValue = this.getValue();
57946             this.fireEvent('focus', this);
57947         }
57948     },
57949
57950     // private
57951     beforeBlur : Ext.emptyFn,
57952
57953     // private
57954     onBlur : function(){
57955         this.beforeBlur();
57956         if(this.focusClass){
57957             this.el.removeClass(this.focusClass);
57958         }
57959         this.hasFocus = false;
57960         if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){
57961             this.validate();
57962         }
57963         var v = this.getValue();
57964         if(String(v) !== String(this.startValue)){
57965             this.fireEvent('change', this, v, this.startValue);
57966         }
57967         this.fireEvent('blur', this);
57968         this.postBlur();
57969     },
57970
57971     // private
57972     postBlur : Ext.emptyFn,
57973
57974     /**
57975      * Returns whether or not the field value is currently valid by
57976      * {@link #validateValue validating} the {@link #processValue processed value}
57977      * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
57978      * @param {Boolean} preventMark True to disable marking the field invalid
57979      * @return {Boolean} True if the value is valid, else false
57980      */
57981     isValid : function(preventMark){
57982         if(this.disabled){
57983             return true;
57984         }
57985         var restore = this.preventMark;
57986         this.preventMark = preventMark === true;
57987         var v = this.validateValue(this.processValue(this.getRawValue()));
57988         this.preventMark = restore;
57989         return v;
57990     },
57991
57992     /**
57993      * Validates the field value
57994      * @return {Boolean} True if the value is valid, else false
57995      */
57996     validate : function(){
57997         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
57998             this.clearInvalid();
57999             return true;
58000         }
58001         return false;
58002     },
58003
58004     /**
58005      * This method should only be overridden if necessary to prepare raw values
58006      * for validation (see {@link #validate} and {@link #isValid}).  This method
58007      * is expected to return the processed value for the field which will
58008      * be used for validation (see validateValue method).
58009      * @param {Mixed} value
58010      */
58011     processValue : function(value){
58012         return value;
58013     },
58014
58015     /**
58016      * Uses getErrors to build an array of validation errors. If any errors are found, markInvalid is called
58017      * with the first and false is returned, otherwise true is returned. Previously, subclasses were invited
58018      * to provide an implementation of this to process validations - from 3.2 onwards getErrors should be
58019      * overridden instead.
58020      * @param {Mixed} The current value of the field
58021      * @return {Boolean} True if all validations passed, false if one or more failed
58022      */
58023      validateValue : function(value) {
58024          //currently, we only show 1 error at a time for a field, so just use the first one
58025          var error = this.getErrors(value)[0];
58026
58027          if (error == undefined) {
58028              return true;
58029          } else {
58030              this.markInvalid(error);
58031              return false;
58032          }
58033      },
58034     
58035     /**
58036      * Runs this field's validators and returns an array of error messages for any validation failures.
58037      * This is called internally during validation and would not usually need to be used manually.
58038      * Each subclass should override or augment the return value to provide their own errors
58039      * @return {Array} All error messages for this field
58040      */
58041     getErrors: function() {
58042         return [];
58043     },
58044
58045     /**
58046      * Gets the active error message for this field.
58047      * @return {String} Returns the active error message on the field, if there is no error, an empty string is returned.
58048      */
58049     getActiveError : function(){
58050         return this.activeError || '';
58051     },
58052
58053     /**
58054      * <p>Display an error message associated with this field, using {@link #msgTarget} to determine how to
58055      * display the message and applying {@link #invalidClass} to the field's UI element.</p>
58056      * <p><b>Note</b>: this method does not cause the Field's {@link #validate} method to return <code>false</code>
58057      * if the value does <i>pass</i> validation. So simply marking a Field as invalid will not prevent
58058      * submission of forms submitted with the {@link Ext.form.Action.Submit#clientValidation} option set.</p>
58059      * {@link #isValid invalid}.
58060      * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
58061      */
58062     markInvalid : function(msg){
58063         //don't set the error icon if we're not rendered or marking is prevented
58064         if (this.rendered && !this.preventMark) {
58065             msg = msg || this.invalidText;
58066
58067             var mt = this.getMessageHandler();
58068             if(mt){
58069                 mt.mark(this, msg);
58070             }else if(this.msgTarget){
58071                 this.el.addClass(this.invalidClass);
58072                 var t = Ext.getDom(this.msgTarget);
58073                 if(t){
58074                     t.innerHTML = msg;
58075                     t.style.display = this.msgDisplay;
58076                 }
58077             }
58078         }
58079         
58080         this.setActiveError(msg);
58081     },
58082     
58083     /**
58084      * Clear any invalid styles/messages for this field
58085      */
58086     clearInvalid : function(){
58087         //don't remove the error icon if we're not rendered or marking is prevented
58088         if (this.rendered && !this.preventMark) {
58089             this.el.removeClass(this.invalidClass);
58090             var mt = this.getMessageHandler();
58091             if(mt){
58092                 mt.clear(this);
58093             }else if(this.msgTarget){
58094                 this.el.removeClass(this.invalidClass);
58095                 var t = Ext.getDom(this.msgTarget);
58096                 if(t){
58097                     t.innerHTML = '';
58098                     t.style.display = 'none';
58099                 }
58100             }
58101         }
58102         
58103         this.unsetActiveError();
58104     },
58105
58106     /**
58107      * Sets the current activeError to the given string. Fires the 'invalid' event.
58108      * This does not set up the error icon, only sets the message and fires the event. To show the error icon,
58109      * use markInvalid instead, which calls this method internally
58110      * @param {String} msg The error message
58111      * @param {Boolean} suppressEvent True to suppress the 'invalid' event from being fired
58112      */
58113     setActiveError: function(msg, suppressEvent) {
58114         this.activeError = msg;
58115         if (suppressEvent !== true) this.fireEvent('invalid', this, msg);
58116     },
58117     
58118     /**
58119      * Clears the activeError and fires the 'valid' event. This is called internally by clearInvalid and would not
58120      * usually need to be called manually
58121      * @param {Boolean} suppressEvent True to suppress the 'invalid' event from being fired
58122      */
58123     unsetActiveError: function(suppressEvent) {
58124         delete this.activeError;
58125         if (suppressEvent !== true) this.fireEvent('valid', this);
58126     },
58127
58128     // private
58129     getMessageHandler : function(){
58130         return Ext.form.MessageTargets[this.msgTarget];
58131     },
58132
58133     // private
58134     getErrorCt : function(){
58135         return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
58136             this.el.findParent('.x-form-field-wrap', 5, true);   // else direct field wrap
58137     },
58138
58139     // Alignment for 'under' target
58140     alignErrorEl : function(){
58141         this.errorEl.setWidth(this.getErrorCt().getWidth(true) - 20);
58142     },
58143
58144     // Alignment for 'side' target
58145     alignErrorIcon : function(){
58146         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
58147     },
58148
58149     /**
58150      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
58151      * @return {Mixed} value The field value
58152      */
58153     getRawValue : function(){
58154         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
58155         if(v === this.emptyText){
58156             v = '';
58157         }
58158         return v;
58159     },
58160
58161     /**
58162      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
58163      * @return {Mixed} value The field value
58164      */
58165     getValue : function(){
58166         if(!this.rendered) {
58167             return this.value;
58168         }
58169         var v = this.el.getValue();
58170         if(v === this.emptyText || v === undefined){
58171             v = '';
58172         }
58173         return v;
58174     },
58175
58176     /**
58177      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
58178      * @param {Mixed} value The value to set
58179      * @return {Mixed} value The field value that is set
58180      */
58181     setRawValue : function(v){
58182         return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
58183     },
58184
58185     /**
58186      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
58187      * @param {Mixed} value The value to set
58188      * @return {Ext.form.Field} this
58189      */
58190     setValue : function(v){
58191         this.value = v;
58192         if(this.rendered){
58193             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
58194             this.validate();
58195         }
58196         return this;
58197     },
58198
58199     // private, does not work for all fields
58200     append : function(v){
58201          this.setValue([this.getValue(), v].join(''));
58202     }
58203
58204     /**
58205      * @cfg {Boolean} autoWidth @hide
58206      */
58207     /**
58208      * @cfg {Boolean} autoHeight @hide
58209      */
58210
58211     /**
58212      * @cfg {String} autoEl @hide
58213      */
58214 });
58215
58216
58217 Ext.form.MessageTargets = {
58218     'qtip' : {
58219         mark: function(field, msg){
58220             field.el.addClass(field.invalidClass);
58221             field.el.dom.qtip = msg;
58222             field.el.dom.qclass = 'x-form-invalid-tip';
58223             if(Ext.QuickTips){ // fix for floating editors interacting with DND
58224                 Ext.QuickTips.enable();
58225             }
58226         },
58227         clear: function(field){
58228             field.el.removeClass(field.invalidClass);
58229             field.el.dom.qtip = '';
58230         }
58231     },
58232     'title' : {
58233         mark: function(field, msg){
58234             field.el.addClass(field.invalidClass);
58235             field.el.dom.title = msg;
58236         },
58237         clear: function(field){
58238             field.el.dom.title = '';
58239         }
58240     },
58241     'under' : {
58242         mark: function(field, msg){
58243             field.el.addClass(field.invalidClass);
58244             if(!field.errorEl){
58245                 var elp = field.getErrorCt();
58246                 if(!elp){ // field has no container el
58247                     field.el.dom.title = msg;
58248                     return;
58249                 }
58250                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
58251                 field.on('resize', field.alignErrorEl, field);
58252                 field.on('destroy', function(){
58253                     Ext.destroy(this.errorEl);
58254                 }, field);
58255             }
58256             field.alignErrorEl();
58257             field.errorEl.update(msg);
58258             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
58259         },
58260         clear: function(field){
58261             field.el.removeClass(field.invalidClass);
58262             if(field.errorEl){
58263                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
58264             }else{
58265                 field.el.dom.title = '';
58266             }
58267         }
58268     },
58269     'side' : {
58270         mark: function(field, msg){
58271             field.el.addClass(field.invalidClass);
58272             if(!field.errorIcon){
58273                 var elp = field.getErrorCt();
58274                 // field has no container el
58275                 if(!elp){
58276                     field.el.dom.title = msg;
58277                     return;
58278                 }
58279                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
58280                 if (field.ownerCt) {
58281                     field.ownerCt.on('afterlayout', field.alignErrorIcon, field);
58282                     field.ownerCt.on('expand', field.alignErrorIcon, field);
58283                 }
58284                 field.on('resize', field.alignErrorIcon, field);
58285                 field.on('destroy', function(){
58286                     Ext.destroy(this.errorIcon);
58287                 }, field);
58288             }
58289             field.alignErrorIcon();
58290             field.errorIcon.dom.qtip = msg;
58291             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
58292             field.errorIcon.show();
58293         },
58294         clear: function(field){
58295             field.el.removeClass(field.invalidClass);
58296             if(field.errorIcon){
58297                 field.errorIcon.dom.qtip = '';
58298                 field.errorIcon.hide();
58299             }else{
58300                 field.el.dom.title = '';
58301             }
58302         }
58303     }
58304 };
58305
58306 // anything other than normal should be considered experimental
58307 Ext.form.Field.msgFx = {
58308     normal : {
58309         show: function(msgEl, f){
58310             msgEl.setDisplayed('block');
58311         },
58312
58313         hide : function(msgEl, f){
58314             msgEl.setDisplayed(false).update('');
58315         }
58316     },
58317
58318     slide : {
58319         show: function(msgEl, f){
58320             msgEl.slideIn('t', {stopFx:true});
58321         },
58322
58323         hide : function(msgEl, f){
58324             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
58325         }
58326     },
58327
58328     slideRight : {
58329         show: function(msgEl, f){
58330             msgEl.fixDisplay();
58331             msgEl.alignTo(f.el, 'tl-tr');
58332             msgEl.slideIn('l', {stopFx:true});
58333         },
58334
58335         hide : function(msgEl, f){
58336             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
58337         }
58338     }
58339 };
58340 Ext.reg('field', Ext.form.Field);
58341 /**
58342  * @class Ext.form.TextField
58343  * @extends Ext.form.Field
58344  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
58345  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
58346  * and {@link Ext.form.ComboBox}).</p>
58347  * <p><b><u>Validation</u></b></p>
58348  * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
58349  * <p><b><u>Alter Validation Behavior</u></b></p>
58350  * <p>Validation behavior for each field can be configured:</p>
58351  * <div class="mdetail-params"><ul>
58352  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
58353  * show if any validation step above does not provide a message when invalid</li>
58354  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
58355  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
58356  * but before being validated</li>
58357  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
58358  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
58359  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
58360  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
58361  * </ul></div>
58362  * 
58363  * @constructor Creates a new TextField
58364  * @param {Object} config Configuration options
58365  * 
58366  * @xtype textfield
58367  */
58368 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
58369     /**
58370      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
58371      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
58372      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
58373      */
58374     /**
58375      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
58376      * before validation (defaults to <tt>null</tt>).
58377      */
58378     /**
58379      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
58380      * (defaults to <tt>false</tt>)
58381      */
58382     grow : false,
58383     /**
58384      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
58385      * to <tt>30</tt>)
58386      */
58387     growMin : 30,
58388     /**
58389      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
58390      * to <tt>800</tt>)
58391      */
58392     growMax : 800,
58393     /**
58394      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
58395      */
58396     vtype : null,
58397     /**
58398      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
58399      * not match (defaults to <tt>null</tt>)
58400      */
58401     maskRe : null,
58402     /**
58403      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
58404      * to <tt>false</tt>)
58405      */
58406     disableKeyFilter : false,
58407     /**
58408      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
58409      * <tt>true</tt>)
58410      */
58411     allowBlank : true,
58412     /**
58413      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
58414      */
58415     minLength : 0,
58416     /**
58417      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
58418      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
58419      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
58420      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
58421      * any attributes you want to a field, for example:<pre><code>
58422 var myField = new Ext.form.NumberField({
58423     id: 'mobile',
58424     anchor:'90%',
58425     fieldLabel: 'Mobile',
58426     maxLength: 16, // for validation
58427     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
58428 });
58429 </code></pre>
58430      */
58431     maxLength : Number.MAX_VALUE,
58432     /**
58433      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
58434      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
58435      */
58436     minLengthText : 'The minimum length for this field is {0}',
58437     /**
58438      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
58439      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
58440      */
58441     maxLengthText : 'The maximum length for this field is {0}',
58442     /**
58443      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
58444      * receives input focus (defaults to <tt>false</tt>)
58445      */
58446     selectOnFocus : false,
58447     /**
58448      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
58449      * fails (defaults to <tt>'This field is required'</tt>)
58450      */
58451     blankText : 'This field is required',
58452     /**
58453      * @cfg {Function} validator
58454      * <p>A custom validation function to be called during field validation ({@link #validateValue})
58455      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
58456      * developer to override the default validation process.</p>
58457      * <br><p>This function will be passed the following Parameters:</p>
58458      * <div class="mdetail-params"><ul>
58459      * <li><code>value</code>: <i>Mixed</i>
58460      * <div class="sub-desc">The current field value</div></li>
58461      * </ul></div>
58462      * <br><p>This function is to Return:</p>
58463      * <div class="mdetail-params"><ul>
58464      * <li><code>true</code>: <i>Boolean</i>
58465      * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
58466      * <li><code>msg</code>: <i>String</i>
58467      * <div class="sub-desc">An error message if the value is invalid</div></li>
58468      * </ul></div>
58469      */
58470     validator : null,
58471     /**
58472      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
58473      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
58474      * <b><tt>{@link #regexText}</tt></b>.
58475      */
58476     regex : null,
58477     /**
58478      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
58479      * test fails during validation (defaults to <tt>''</tt>)
58480      */
58481     regexText : '',
58482     /**
58483      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
58484      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
58485      * with a {@link #name}.
58486      */
58487     emptyText : null,
58488     /**
58489      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
58490      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
58491      * depending on the current field value.
58492      */
58493     emptyClass : 'x-form-empty-field',
58494
58495     /**
58496      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
58497      * field (defaults to <tt>false</tt>)
58498      */
58499
58500     initComponent : function(){
58501         Ext.form.TextField.superclass.initComponent.call(this);
58502         this.addEvents(
58503             /**
58504              * @event autosize
58505              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
58506              * may not have actually changed size according to the default logic, but this event provides
58507              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
58508              * @param {Ext.form.Field} this This text field
58509              * @param {Number} width The new field width
58510              */
58511             'autosize',
58512
58513             /**
58514              * @event keydown
58515              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
58516              * is set to true.
58517              * @param {Ext.form.TextField} this This text field
58518              * @param {Ext.EventObject} e
58519              */
58520             'keydown',
58521             /**
58522              * @event keyup
58523              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
58524              * is set to true.
58525              * @param {Ext.form.TextField} this This text field
58526              * @param {Ext.EventObject} e
58527              */
58528             'keyup',
58529             /**
58530              * @event keypress
58531              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
58532              * is set to true.
58533              * @param {Ext.form.TextField} this This text field
58534              * @param {Ext.EventObject} e
58535              */
58536             'keypress'
58537         );
58538     },
58539
58540     // private
58541     initEvents : function(){
58542         Ext.form.TextField.superclass.initEvents.call(this);
58543         if(this.validationEvent == 'keyup'){
58544             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
58545             this.mon(this.el, 'keyup', this.filterValidation, this);
58546         }
58547         else if(this.validationEvent !== false && this.validationEvent != 'blur'){
58548                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
58549         }
58550         if(this.selectOnFocus || this.emptyText){            
58551             this.mon(this.el, 'mousedown', this.onMouseDown, this);
58552             
58553             if(this.emptyText){
58554                 this.applyEmptyText();
58555             }
58556         }
58557         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
58558                 this.mon(this.el, 'keypress', this.filterKeys, this);
58559         }
58560         if(this.grow){
58561                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
58562                         this.mon(this.el, 'click', this.autoSize, this);
58563         }
58564         if(this.enableKeyEvents){
58565             this.mon(this.el, {
58566                 scope: this,
58567                 keyup: this.onKeyUp,
58568                 keydown: this.onKeyDown,
58569                 keypress: this.onKeyPress
58570             });
58571         }
58572     },
58573     
58574     onMouseDown: function(e){
58575         if(!this.hasFocus){
58576             this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
58577         }
58578     },
58579
58580     processValue : function(value){
58581         if(this.stripCharsRe){
58582             var newValue = value.replace(this.stripCharsRe, '');
58583             if(newValue !== value){
58584                 this.setRawValue(newValue);
58585                 return newValue;
58586             }
58587         }
58588         return value;
58589     },
58590
58591     filterValidation : function(e){
58592         if(!e.isNavKeyPress()){
58593             this.validationTask.delay(this.validationDelay);
58594         }
58595     },
58596     
58597     //private
58598     onDisable: function(){
58599         Ext.form.TextField.superclass.onDisable.call(this);
58600         if(Ext.isIE){
58601             this.el.dom.unselectable = 'on';
58602         }
58603     },
58604     
58605     //private
58606     onEnable: function(){
58607         Ext.form.TextField.superclass.onEnable.call(this);
58608         if(Ext.isIE){
58609             this.el.dom.unselectable = '';
58610         }
58611     },
58612
58613     // private
58614     onKeyUpBuffered : function(e){
58615         if(this.doAutoSize(e)){
58616             this.autoSize();
58617         }
58618     },
58619     
58620     // private
58621     doAutoSize : function(e){
58622         return !e.isNavKeyPress();
58623     },
58624
58625     // private
58626     onKeyUp : function(e){
58627         this.fireEvent('keyup', this, e);
58628     },
58629
58630     // private
58631     onKeyDown : function(e){
58632         this.fireEvent('keydown', this, e);
58633     },
58634
58635     // private
58636     onKeyPress : function(e){
58637         this.fireEvent('keypress', this, e);
58638     },
58639
58640     /**
58641      * Resets the current field value to the originally-loaded value and clears any validation messages.
58642      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
58643      * original value was blank.
58644      */
58645     reset : function(){
58646         Ext.form.TextField.superclass.reset.call(this);
58647         this.applyEmptyText();
58648     },
58649
58650     applyEmptyText : function(){
58651         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
58652             this.setRawValue(this.emptyText);
58653             this.el.addClass(this.emptyClass);
58654         }
58655     },
58656
58657     // private
58658     preFocus : function(){
58659         var el = this.el;
58660         if(this.emptyText){
58661             if(el.dom.value == this.emptyText){
58662                 this.setRawValue('');
58663             }
58664             el.removeClass(this.emptyClass);
58665         }
58666         if(this.selectOnFocus){
58667             el.dom.select();
58668         }
58669     },
58670
58671     // private
58672     postBlur : function(){
58673         this.applyEmptyText();
58674     },
58675
58676     // private
58677     filterKeys : function(e){
58678         if(e.ctrlKey){
58679             return;
58680         }
58681         var k = e.getKey();
58682         if(Ext.isGecko && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
58683             return;
58684         }
58685         var cc = String.fromCharCode(e.getCharCode());
58686         if(!Ext.isGecko && e.isSpecialKey() && !cc){
58687             return;
58688         }
58689         if(!this.maskRe.test(cc)){
58690             e.stopEvent();
58691         }
58692     },
58693
58694     setValue : function(v){
58695         if(this.emptyText && this.el && !Ext.isEmpty(v)){
58696             this.el.removeClass(this.emptyClass);
58697         }
58698         Ext.form.TextField.superclass.setValue.apply(this, arguments);
58699         this.applyEmptyText();
58700         this.autoSize();
58701         return this;
58702     },
58703
58704     /**
58705      * <p>Validates a value according to the field's validation rules and returns an array of errors
58706      * for any failing validations. Validation rules are processed in the following order:</p>
58707      * <div class="mdetail-params"><ul>
58708      * 
58709      * <li><b>1. Field specific validator</b>
58710      * <div class="sub-desc">
58711      * <p>A validator offers a way to customize and reuse a validation specification.
58712      * If a field is configured with a <code>{@link #validator}</code>
58713      * function, it will be passed the current field value.  The <code>{@link #validator}</code>
58714      * function is expected to return either:
58715      * <div class="mdetail-params"><ul>
58716      * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
58717      * <li>a String to represent the invalid message if invalid (validation halts).</li>
58718      * </ul></div>
58719      * </div></li>
58720      * 
58721      * <li><b>2. Basic Validation</b>
58722      * <div class="sub-desc">
58723      * <p>If the <code>{@link #validator}</code> has not halted validation,
58724      * basic validation proceeds as follows:</p>
58725      * 
58726      * <div class="mdetail-params"><ul>
58727      * 
58728      * <li><code>{@link #allowBlank}</code> : (Invalid message =
58729      * <code>{@link #emptyText}</code>)<div class="sub-desc">
58730      * Depending on the configuration of <code>{@link #allowBlank}</code>, a
58731      * blank field will cause validation to halt at this step and return
58732      * Boolean true or false accordingly.  
58733      * </div></li>
58734      * 
58735      * <li><code>{@link #minLength}</code> : (Invalid message =
58736      * <code>{@link #minLengthText}</code>)<div class="sub-desc">
58737      * If the passed value does not satisfy the <code>{@link #minLength}</code>
58738      * specified, validation halts.
58739      * </div></li>
58740      * 
58741      * <li><code>{@link #maxLength}</code> : (Invalid message =
58742      * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
58743      * If the passed value does not satisfy the <code>{@link #maxLength}</code>
58744      * specified, validation halts.
58745      * </div></li>
58746      * 
58747      * </ul></div>
58748      * </div></li>
58749      * 
58750      * <li><b>3. Preconfigured Validation Types (VTypes)</b>
58751      * <div class="sub-desc">
58752      * <p>If none of the prior validation steps halts validation, a field
58753      * configured with a <code>{@link #vtype}</code> will utilize the
58754      * corresponding {@link Ext.form.VTypes VTypes} validation function.
58755      * If invalid, either the field's <code>{@link #vtypeText}</code> or
58756      * the VTypes vtype Text property will be used for the invalid message.
58757      * Keystrokes on the field will be filtered according to the VTypes
58758      * vtype Mask property.</p>
58759      * </div></li>
58760      * 
58761      * <li><b>4. Field specific regex test</b>
58762      * <div class="sub-desc">
58763      * <p>If none of the prior validation steps halts validation, a field's
58764      * configured <code>{@link #regex}</code> test will be processed.
58765      * The invalid message for this test is configured with
58766      * <code>{@link #regexText}</code>.</p>
58767      * </div></li>
58768      * 
58769      * @param {Mixed} value The value to validate. The processed raw value will be used if nothing is passed
58770      * @return {Array} Array of any validation errors
58771      */
58772     getErrors: function(value) {
58773         var errors = Ext.form.TextField.superclass.getErrors.apply(this, arguments);
58774         
58775         value = value || this.processValue(this.getRawValue());        
58776         
58777         if(Ext.isFunction(this.validator)){
58778             var msg = this.validator(value);
58779             if (msg !== true) {
58780                 errors.push(msg);
58781             }
58782         }
58783         
58784         if (!this.allowBlank && (value.length < 1 || value === this.emptyText)) { // if it's blank
58785             errors.push(this.blankText);
58786         }
58787         
58788         if (value.length < this.minLength) {
58789             errors.push(String.format(this.minLengthText, this.minLength));
58790         }
58791         
58792         if (value.length > this.maxLength) {
58793             errors.push(String.format(this.maxLengthText, this.maxLength));
58794         }
58795         
58796         if (this.vtype) {
58797             var vt = Ext.form.VTypes;
58798             if(!vt[this.vtype](value, this)){
58799                 errors.push(this.vtypeText || vt[this.vtype +'Text']);
58800             }
58801         }
58802         
58803         if (this.regex && !this.regex.test(value)) {
58804             errors.push(this.regexText);
58805         }
58806         
58807         return errors;
58808     },
58809
58810     /**
58811      * Selects text in this field
58812      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
58813      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
58814      */
58815     selectText : function(start, end){
58816         var v = this.getRawValue();
58817         var doFocus = false;
58818         if(v.length > 0){
58819             start = start === undefined ? 0 : start;
58820             end = end === undefined ? v.length : end;
58821             var d = this.el.dom;
58822             if(d.setSelectionRange){
58823                 d.setSelectionRange(start, end);
58824             }else if(d.createTextRange){
58825                 var range = d.createTextRange();
58826                 range.moveStart('character', start);
58827                 range.moveEnd('character', end-v.length);
58828                 range.select();
58829             }
58830             doFocus = Ext.isGecko || Ext.isOpera;
58831         }else{
58832             doFocus = true;
58833         }
58834         if(doFocus){
58835             this.focus();
58836         }
58837     },
58838
58839     /**
58840      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
58841      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
58842      */
58843     autoSize : function(){
58844         if(!this.grow || !this.rendered){
58845             return;
58846         }
58847         if(!this.metrics){
58848             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
58849         }
58850         var el = this.el;
58851         var v = el.dom.value;
58852         var d = document.createElement('div');
58853         d.appendChild(document.createTextNode(v));
58854         v = d.innerHTML;
58855         Ext.removeNode(d);
58856         d = null;
58857         v += '&#160;';
58858         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
58859         this.el.setWidth(w);
58860         this.fireEvent('autosize', this, w);
58861     },
58862         
58863         onDestroy: function(){
58864                 if(this.validationTask){
58865                         this.validationTask.cancel();
58866                         this.validationTask = null;
58867                 }
58868                 Ext.form.TextField.superclass.onDestroy.call(this);
58869         }
58870 });
58871 Ext.reg('textfield', Ext.form.TextField);
58872 /**
58873  * @class Ext.form.TriggerField
58874  * @extends Ext.form.TextField
58875  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
58876  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
58877  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
58878  * for which you can provide a custom implementation.  For example:
58879  * <pre><code>
58880 var trigger = new Ext.form.TriggerField();
58881 trigger.onTriggerClick = myTriggerFn;
58882 trigger.applyToMarkup('my-field');
58883 </code></pre>
58884  *
58885  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
58886  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
58887  *
58888  * @constructor
58889  * Create a new TriggerField.
58890  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
58891  * to the base TextField)
58892  * @xtype trigger
58893  */
58894 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {
58895     /**
58896      * @cfg {String} triggerClass
58897      * An additional CSS class used to style the trigger button.  The trigger will always get the
58898      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
58899      */
58900     /**
58901      * @cfg {Mixed} triggerConfig
58902      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
58903      * trigger element for this Field. (Optional).</p>
58904      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
58905      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
58906      * and appearance of the trigger.  Defaults to:</p>
58907      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
58908      */
58909     /**
58910      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
58911      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
58912      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
58913      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
58914      */
58915     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
58916     /**
58917      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
58918      * text field (defaults to <tt>false</tt>)
58919      */
58920     hideTrigger:false,
58921     /**
58922      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
58923      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>).
58924      */
58925     editable: true,
58926     /**
58927      * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
58928      * hides the trigger.  Superceeds the editable and hideTrigger options if the value is true.
58929      * (defaults to <tt>false</tt>)
58930      */
58931     readOnly: false,
58932     /**
58933      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
58934      * <tt>x-trigger-wrap-focus</tt>.
58935      */
58936     wrapFocusClass: 'x-trigger-wrap-focus',
58937     /**
58938      * @hide
58939      * @method autoSize
58940      */
58941     autoSize: Ext.emptyFn,
58942     // private
58943     monitorTab : true,
58944     // private
58945     deferHeight : true,
58946     // private
58947     mimicing : false,
58948
58949     actionMode: 'wrap',
58950
58951     defaultTriggerWidth: 17,
58952
58953     // private
58954     onResize : function(w, h){
58955         Ext.form.TriggerField.superclass.onResize.call(this, w, h);
58956         var tw = this.getTriggerWidth();
58957         if(Ext.isNumber(w)){
58958             this.el.setWidth(w - tw);
58959         }
58960         this.wrap.setWidth(this.el.getWidth() + tw);
58961     },
58962
58963     getTriggerWidth: function(){
58964         var tw = this.trigger.getWidth();
58965         if(!this.hideTrigger && !this.readOnly && tw === 0){
58966             tw = this.defaultTriggerWidth;
58967         }
58968         return tw;
58969     },
58970
58971     // private
58972     alignErrorIcon : function(){
58973         if(this.wrap){
58974             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
58975         }
58976     },
58977
58978     // private
58979     onRender : function(ct, position){
58980         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
58981         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
58982
58983         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
58984         this.trigger = this.wrap.createChild(this.triggerConfig ||
58985                 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
58986         this.initTrigger();
58987         if(!this.width){
58988             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
58989         }
58990         this.resizeEl = this.positionEl = this.wrap;
58991     },
58992
58993     getWidth: function() {
58994         return(this.el.getWidth() + this.trigger.getWidth());
58995     },
58996
58997     updateEditState: function(){
58998         if(this.rendered){
58999             if (this.readOnly) {
59000                 this.el.dom.readOnly = true;
59001                 this.el.addClass('x-trigger-noedit');
59002                 this.mun(this.el, 'click', this.onTriggerClick, this);
59003                 this.trigger.setDisplayed(false);
59004             } else {
59005                 if (!this.editable) {
59006                     this.el.dom.readOnly = true;
59007                     this.el.addClass('x-trigger-noedit');
59008                     this.mon(this.el, 'click', this.onTriggerClick, this);
59009                 } else {
59010                     this.el.dom.readOnly = false;
59011                     this.el.removeClass('x-trigger-noedit');
59012                     this.mun(this.el, 'click', this.onTriggerClick, this);
59013                 }
59014                 this.trigger.setDisplayed(!this.hideTrigger);
59015             }
59016             this.onResize(this.width || this.wrap.getWidth());
59017         }
59018     },
59019
59020     setHideTrigger: function(hideTrigger){
59021         if(hideTrigger != this.hideTrigger){
59022             this.hideTrigger = hideTrigger;
59023             this.updateEditState();
59024         }
59025     },
59026
59027     /**
59028      * @param {Boolean} value True to allow the user to directly edit the field text
59029      * Allow or prevent the user from directly editing the field text.  If false is passed,
59030      * the user will only be able to modify the field using the trigger.  Will also add
59031      * a click event to the text field which will call the trigger. This method
59032      * is the runtime equivalent of setting the 'editable' config option at config time.
59033      */
59034     setEditable: function(editable){
59035         if(editable != this.editable){
59036             this.editable = editable;
59037             this.updateEditState();
59038         }
59039     },
59040
59041     /**
59042      * @param {Boolean} value True to prevent the user changing the field and explicitly
59043      * hide the trigger.
59044      * Setting this to true will superceed settings editable and hideTrigger.
59045      * Setting this to false will defer back to editable and hideTrigger. This method
59046      * is the runtime equivalent of setting the 'readOnly' config option at config time.
59047      */
59048     setReadOnly: function(readOnly){
59049         if(readOnly != this.readOnly){
59050             this.readOnly = readOnly;
59051             this.updateEditState();
59052         }
59053     },
59054
59055     afterRender : function(){
59056         Ext.form.TriggerField.superclass.afterRender.call(this);
59057         this.updateEditState();
59058     },
59059
59060     // private
59061     initTrigger : function(){
59062         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
59063         this.trigger.addClassOnOver('x-form-trigger-over');
59064         this.trigger.addClassOnClick('x-form-trigger-click');
59065     },
59066
59067     // private
59068     onDestroy : function(){
59069         Ext.destroy(this.trigger, this.wrap);
59070         if (this.mimicing){
59071             this.doc.un('mousedown', this.mimicBlur, this);
59072         }
59073         delete this.doc;
59074         Ext.form.TriggerField.superclass.onDestroy.call(this);
59075     },
59076
59077     // private
59078     onFocus : function(){
59079         Ext.form.TriggerField.superclass.onFocus.call(this);
59080         if(!this.mimicing){
59081             this.wrap.addClass(this.wrapFocusClass);
59082             this.mimicing = true;
59083             this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
59084             if(this.monitorTab){
59085                 this.on('specialkey', this.checkTab, this);
59086             }
59087         }
59088     },
59089
59090     // private
59091     checkTab : function(me, e){
59092         if(e.getKey() == e.TAB){
59093             this.triggerBlur();
59094         }
59095     },
59096
59097     // private
59098     onBlur : Ext.emptyFn,
59099
59100     // private
59101     mimicBlur : function(e){
59102         if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
59103             this.triggerBlur();
59104         }
59105     },
59106
59107     // private
59108     triggerBlur : function(){
59109         this.mimicing = false;
59110         this.doc.un('mousedown', this.mimicBlur, this);
59111         if(this.monitorTab && this.el){
59112             this.un('specialkey', this.checkTab, this);
59113         }
59114         Ext.form.TriggerField.superclass.onBlur.call(this);
59115         if(this.wrap){
59116             this.wrap.removeClass(this.wrapFocusClass);
59117         }
59118     },
59119
59120     beforeBlur : Ext.emptyFn,
59121
59122     // private
59123     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
59124     validateBlur : function(e){
59125         return true;
59126     },
59127
59128     /**
59129      * The function that should handle the trigger's click event.  This method does nothing by default
59130      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for
59131      * sample implementations.
59132      * @method
59133      * @param {EventObject} e
59134      */
59135     onTriggerClick : Ext.emptyFn
59136
59137     /**
59138      * @cfg {Boolean} grow @hide
59139      */
59140     /**
59141      * @cfg {Number} growMin @hide
59142      */
59143     /**
59144      * @cfg {Number} growMax @hide
59145      */
59146 });
59147
59148 /**
59149  * @class Ext.form.TwinTriggerField
59150  * @extends Ext.form.TriggerField
59151  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
59152  * to be extended by an implementing class.  For an example of implementing this class, see the custom
59153  * SearchField implementation here:
59154  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
59155  */
59156 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
59157     /**
59158      * @cfg {Mixed} triggerConfig
59159      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
59160      * for this Field. (Optional).</p>
59161      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
59162      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
59163      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
59164      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
59165      * positioning and appearance of the triggers.</p>
59166      */
59167     /**
59168      * @cfg {String} trigger1Class
59169      * An additional CSS class used to style the trigger button.  The trigger will always get the
59170      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
59171      */
59172     /**
59173      * @cfg {String} trigger2Class
59174      * An additional CSS class used to style the trigger button.  The trigger will always get the
59175      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
59176      */
59177
59178     initComponent : function(){
59179         Ext.form.TwinTriggerField.superclass.initComponent.call(this);
59180
59181         this.triggerConfig = {
59182             tag:'span', cls:'x-form-twin-triggers', cn:[
59183             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
59184             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
59185         ]};
59186     },
59187
59188     getTrigger : function(index){
59189         return this.triggers[index];
59190     },
59191
59192     initTrigger : function(){
59193         var ts = this.trigger.select('.x-form-trigger', true);
59194         var triggerField = this;
59195         ts.each(function(t, all, index){
59196             var triggerIndex = 'Trigger'+(index+1);
59197             t.hide = function(){
59198                 var w = triggerField.wrap.getWidth();
59199                 this.dom.style.display = 'none';
59200                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
59201                 this['hidden' + triggerIndex] = true;
59202             };
59203             t.show = function(){
59204                 var w = triggerField.wrap.getWidth();
59205                 this.dom.style.display = '';
59206                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
59207                 this['hidden' + triggerIndex] = false;
59208             };
59209
59210             if(this['hide'+triggerIndex]){
59211                 t.dom.style.display = 'none';
59212                 this['hidden' + triggerIndex] = true;
59213             }
59214             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
59215             t.addClassOnOver('x-form-trigger-over');
59216             t.addClassOnClick('x-form-trigger-click');
59217         }, this);
59218         this.triggers = ts.elements;
59219     },
59220
59221     getTriggerWidth: function(){
59222         var tw = 0;
59223         Ext.each(this.triggers, function(t, index){
59224             var triggerIndex = 'Trigger' + (index + 1),
59225                 w = t.getWidth();
59226             if(w === 0 && !this['hidden' + triggerIndex]){
59227                 tw += this.defaultTriggerWidth;
59228             }else{
59229                 tw += w;
59230             }
59231         }, this);
59232         return tw;
59233     },
59234
59235     // private
59236     onDestroy : function() {
59237         Ext.destroy(this.triggers);
59238         Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
59239     },
59240
59241     /**
59242      * The function that should handle the trigger's click event.  This method does nothing by default
59243      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
59244      * for additional information.
59245      * @method
59246      * @param {EventObject} e
59247      */
59248     onTrigger1Click : Ext.emptyFn,
59249     /**
59250      * The function that should handle the trigger's click event.  This method does nothing by default
59251      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
59252      * for additional information.
59253      * @method
59254      * @param {EventObject} e
59255      */
59256     onTrigger2Click : Ext.emptyFn
59257 });
59258 Ext.reg('trigger', Ext.form.TriggerField);
59259 /**
59260  * @class Ext.form.TextArea
59261  * @extends Ext.form.TextField
59262  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
59263  * support for auto-sizing.
59264  * @constructor
59265  * Creates a new TextArea
59266  * @param {Object} config Configuration options
59267  * @xtype textarea
59268  */
59269 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
59270     /**
59271      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
59272      * (defaults to <tt>60</tt>)
59273      */
59274     growMin : 60,
59275     /**
59276      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
59277      * (defaults to <tt>1000</tt>)
59278      */
59279     growMax: 1000,
59280     growAppend : '&#160;\n&#160;',
59281
59282     enterIsSpecial : false,
59283
59284     /**
59285      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
59286      * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to 
59287      * <tt>false</tt>.
59288      */
59289     preventScrollbars: false,
59290     /**
59291      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
59292      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
59293      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
59294      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
59295      */
59296
59297     // private
59298     onRender : function(ct, position){
59299         if(!this.el){
59300             this.defaultAutoCreate = {
59301                 tag: "textarea",
59302                 style:"width:100px;height:60px;",
59303                 autocomplete: "off"
59304             };
59305         }
59306         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
59307         if(this.grow){
59308             this.textSizeEl = Ext.DomHelper.append(document.body, {
59309                 tag: "pre", cls: "x-form-grow-sizer"
59310             });
59311             if(this.preventScrollbars){
59312                 this.el.setStyle("overflow", "hidden");
59313             }
59314             this.el.setHeight(this.growMin);
59315         }
59316     },
59317
59318     onDestroy : function(){
59319         Ext.removeNode(this.textSizeEl);
59320         Ext.form.TextArea.superclass.onDestroy.call(this);
59321     },
59322
59323     fireKey : function(e){
59324         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
59325             this.fireEvent("specialkey", this, e);
59326         }
59327     },
59328     
59329     // private
59330     doAutoSize : function(e){
59331         return !e.isNavKeyPress() || e.getKey() == e.ENTER;
59332     },
59333
59334     /**
59335      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
59336      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
59337      */
59338     autoSize: function(){
59339         if(!this.grow || !this.textSizeEl){
59340             return;
59341         }
59342         var el = this.el,
59343             v = Ext.util.Format.htmlEncode(el.dom.value),
59344             ts = this.textSizeEl,
59345             h;
59346             
59347         Ext.fly(ts).setWidth(this.el.getWidth());
59348         if(v.length < 1){
59349             v = "&#160;&#160;";
59350         }else{
59351             v += this.growAppend;
59352             if(Ext.isIE){
59353                 v = v.replace(/\n/g, '&#160;<br />');
59354             }
59355         }
59356         ts.innerHTML = v;
59357         h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
59358         if(h != this.lastHeight){
59359             this.lastHeight = h;
59360             this.el.setHeight(h);
59361             this.fireEvent("autosize", this, h);
59362         }
59363     }
59364 });
59365 Ext.reg('textarea', Ext.form.TextArea);/**
59366  * @class Ext.form.NumberField
59367  * @extends Ext.form.TextField
59368  * Numeric text field that provides automatic keystroke filtering and numeric validation.
59369  * @constructor
59370  * Creates a new NumberField
59371  * @param {Object} config Configuration options
59372  * @xtype numberfield
59373  */
59374 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
59375     /**
59376      * @cfg {RegExp} stripCharsRe @hide
59377      */
59378     /**
59379      * @cfg {RegExp} maskRe @hide
59380      */
59381     /**
59382      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
59383      */
59384     fieldClass: "x-form-field x-form-num-field",
59385     /**
59386      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
59387      */
59388     allowDecimals : true,
59389     /**
59390      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
59391      */
59392     decimalSeparator : ".",
59393     /**
59394      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
59395      */
59396     decimalPrecision : 2,
59397     /**
59398      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
59399      */
59400     allowNegative : true,
59401     /**
59402      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
59403      */
59404     minValue : Number.NEGATIVE_INFINITY,
59405     /**
59406      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
59407      */
59408     maxValue : Number.MAX_VALUE,
59409     /**
59410      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
59411      */
59412     minText : "The minimum value for this field is {0}",
59413     /**
59414      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
59415      */
59416     maxText : "The maximum value for this field is {0}",
59417     /**
59418      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
59419      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
59420      */
59421     nanText : "{0} is not a valid number",
59422     /**
59423      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
59424      */
59425     baseChars : "0123456789",
59426
59427     // private
59428     initEvents : function(){
59429         var allowed = this.baseChars + '';
59430         if (this.allowDecimals) {
59431             allowed += this.decimalSeparator;
59432         }
59433         if (this.allowNegative) {
59434             allowed += '-';
59435         }
59436         this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
59437         Ext.form.NumberField.superclass.initEvents.call(this);
59438     },
59439     
59440     /**
59441      * Runs all of NumberFields validations and returns an array of any errors. Note that this first
59442      * runs TextField's validations, so the returned array is an amalgamation of all field errors.
59443      * The additional validations run test that the value is a number, and that it is within the
59444      * configured min and max values.
59445      * @param {Mixed} value The value to get errors for (defaults to the current field value)
59446      * @return {Array} All validation errors for this field
59447      */
59448     getErrors: function(value) {
59449         var errors = Ext.form.NumberField.superclass.getErrors.apply(this, arguments);
59450         
59451         value = value || this.processValue(this.getRawValue());
59452         
59453         if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
59454              return errors;
59455         }
59456         
59457         value = String(value).replace(this.decimalSeparator, ".");
59458         
59459         if(isNaN(value)){
59460             errors.push(String.format(this.nanText, value));
59461         }
59462         
59463         var num = this.parseValue(value);
59464         
59465         if(num < this.minValue){
59466             errors.push(String.format(this.minText, this.minValue));
59467         }
59468         
59469         if(num > this.maxValue){
59470             errors.push(String.format(this.maxText, this.maxValue));
59471         }
59472         
59473         return errors;
59474     },
59475
59476     getValue : function(){
59477         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
59478     },
59479
59480     setValue : function(v){
59481         v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
59482         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
59483         return Ext.form.NumberField.superclass.setValue.call(this, v);
59484     },
59485     
59486     /**
59487      * Replaces any existing {@link #minValue} with the new value.
59488      * @param {Number} value The minimum value
59489      */
59490     setMinValue : function(value){
59491         this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
59492     },
59493     
59494     /**
59495      * Replaces any existing {@link #maxValue} with the new value.
59496      * @param {Number} value The maximum value
59497      */
59498     setMaxValue : function(value){
59499         this.maxValue = Ext.num(value, Number.MAX_VALUE);    
59500     },
59501
59502     // private
59503     parseValue : function(value){
59504         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
59505         return isNaN(value) ? '' : value;
59506     },
59507
59508     // private
59509     fixPrecision : function(value){
59510         var nan = isNaN(value);
59511         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
59512            return nan ? '' : value;
59513         }
59514         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
59515     },
59516
59517     beforeBlur : function(){
59518         var v = this.parseValue(this.getRawValue());
59519         if(!Ext.isEmpty(v)){
59520             this.setValue(this.fixPrecision(v));
59521         }
59522     }
59523 });
59524 Ext.reg('numberfield', Ext.form.NumberField);/**
59525  * @class Ext.form.DateField
59526  * @extends Ext.form.TriggerField
59527  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
59528  * @constructor
59529  * Create a new DateField
59530  * @param {Object} config
59531  * @xtype datefield
59532  */
59533 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
59534     /**
59535      * @cfg {String} format
59536      * The default date format string which can be overriden for localization support.  The format must be
59537      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
59538      */
59539     format : "m/d/Y",
59540     /**
59541      * @cfg {String} altFormats
59542      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
59543      * does not match the defined format (defaults to
59544      * <tt>'m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d'</tt>).
59545      */
59546     altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d",
59547     /**
59548      * @cfg {String} disabledDaysText
59549      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
59550      */
59551     disabledDaysText : "Disabled",
59552     /**
59553      * @cfg {String} disabledDatesText
59554      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
59555      */
59556     disabledDatesText : "Disabled",
59557     /**
59558      * @cfg {String} minText
59559      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
59560      * <tt>'The date in this field must be after {minValue}'</tt>).
59561      */
59562     minText : "The date in this field must be equal to or after {0}",
59563     /**
59564      * @cfg {String} maxText
59565      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
59566      * <tt>'The date in this field must be before {maxValue}'</tt>).
59567      */
59568     maxText : "The date in this field must be equal to or before {0}",
59569     /**
59570      * @cfg {String} invalidText
59571      * The error text to display when the date in the field is invalid (defaults to
59572      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
59573      */
59574     invalidText : "{0} is not a valid date - it must be in the format {1}",
59575     /**
59576      * @cfg {String} triggerClass
59577      * An additional CSS class used to style the trigger button.  The trigger will always get the
59578      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
59579      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
59580      */
59581     triggerClass : 'x-form-date-trigger',
59582     /**
59583      * @cfg {Boolean} showToday
59584      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
59585      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
59586      */
59587     showToday : true,
59588     /**
59589      * @cfg {Date/String} minValue
59590      * The minimum allowed date. Can be either a Javascript date object or a string date in a
59591      * valid format (defaults to null).
59592      */
59593     /**
59594      * @cfg {Date/String} maxValue
59595      * The maximum allowed date. Can be either a Javascript date object or a string date in a
59596      * valid format (defaults to null).
59597      */
59598     /**
59599      * @cfg {Array} disabledDays
59600      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
59601 // disable Sunday and Saturday:
59602 disabledDays:  [0, 6]
59603 // disable weekdays:
59604 disabledDays: [1,2,3,4,5]
59605      * </code></pre>
59606      */
59607     /**
59608      * @cfg {Array} disabledDates
59609      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
59610      * expression so they are very powerful. Some examples:<pre><code>
59611 // disable these exact dates:
59612 disabledDates: ["03/08/2003", "09/16/2003"]
59613 // disable these days for every year:
59614 disabledDates: ["03/08", "09/16"]
59615 // only match the beginning (useful if you are using short years):
59616 disabledDates: ["^03/08"]
59617 // disable every day in March 2006:
59618 disabledDates: ["03/../2006"]
59619 // disable every day in every March:
59620 disabledDates: ["^03"]
59621      * </code></pre>
59622      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
59623      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
59624      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
59625      */
59626     /**
59627      * @cfg {String/Object} autoCreate
59628      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
59629      * specification object:<pre><code>
59630      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
59631      * </code></pre>
59632      */
59633
59634     // private
59635     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
59636
59637     // in the absence of a time value, a default value of 12 noon will be used
59638     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
59639     initTime: '12', // 24 hour format
59640
59641     initTimeFormat: 'H',
59642
59643     // PUBLIC -- to be documented
59644     safeParse : function(value, format) {
59645         if (/[gGhH]/.test(format.replace(/(\\.)/g, ''))) {
59646             // if parse format contains hour information, no DST adjustment is necessary
59647             return Date.parseDate(value, format);
59648         } else {
59649             // set time to 12 noon, then clear the time
59650             var parsedDate = Date.parseDate(value + ' ' + this.initTime, format + ' ' + this.initTimeFormat);
59651             
59652             if (parsedDate) return parsedDate.clearTime();
59653         }
59654     },
59655
59656     initComponent : function(){
59657         Ext.form.DateField.superclass.initComponent.call(this);
59658
59659         this.addEvents(
59660             /**
59661              * @event select
59662              * Fires when a date is selected via the date picker.
59663              * @param {Ext.form.DateField} this
59664              * @param {Date} date The date that was selected
59665              */
59666             'select'
59667         );
59668
59669         if(Ext.isString(this.minValue)){
59670             this.minValue = this.parseDate(this.minValue);
59671         }
59672         if(Ext.isString(this.maxValue)){
59673             this.maxValue = this.parseDate(this.maxValue);
59674         }
59675         this.disabledDatesRE = null;
59676         this.initDisabledDays();
59677     },
59678
59679     initEvents: function() {
59680         Ext.form.DateField.superclass.initEvents.call(this);
59681         this.keyNav = new Ext.KeyNav(this.el, {
59682             "down": function(e) {
59683                 this.onTriggerClick();
59684             },
59685             scope: this,
59686             forceKeyDown: true
59687         });
59688     },
59689
59690
59691     // private
59692     initDisabledDays : function(){
59693         if(this.disabledDates){
59694             var dd = this.disabledDates,
59695                 len = dd.length - 1,
59696                 re = "(?:";
59697
59698             Ext.each(dd, function(d, i){
59699                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
59700                 if(i != len){
59701                     re += '|';
59702                 }
59703             }, this);
59704             this.disabledDatesRE = new RegExp(re + ')');
59705         }
59706     },
59707
59708     /**
59709      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
59710      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
59711      * for details on supported values) used to disable a pattern of dates.
59712      */
59713     setDisabledDates : function(dd){
59714         this.disabledDates = dd;
59715         this.initDisabledDays();
59716         if(this.menu){
59717             this.menu.picker.setDisabledDates(this.disabledDatesRE);
59718         }
59719     },
59720
59721     /**
59722      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
59723      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
59724      * config for details on supported values.
59725      */
59726     setDisabledDays : function(dd){
59727         this.disabledDays = dd;
59728         if(this.menu){
59729             this.menu.picker.setDisabledDays(dd);
59730         }
59731     },
59732
59733     /**
59734      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
59735      * @param {Date} value The minimum date that can be selected
59736      */
59737     setMinValue : function(dt){
59738         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
59739         if(this.menu){
59740             this.menu.picker.setMinDate(this.minValue);
59741         }
59742     },
59743
59744     /**
59745      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
59746      * @param {Date} value The maximum date that can be selected
59747      */
59748     setMaxValue : function(dt){
59749         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
59750         if(this.menu){
59751             this.menu.picker.setMaxDate(this.maxValue);
59752         }
59753     },
59754     
59755     /**
59756      * Runs all of NumberFields validations and returns an array of any errors. Note that this first
59757      * runs TextField's validations, so the returned array is an amalgamation of all field errors.
59758      * The additional validation checks are testing that the date format is valid, that the chosen
59759      * date is within the min and max date constraints set, that the date chosen is not in the disabledDates
59760      * regex and that the day chosed is not one of the disabledDays.
59761      * @param {Mixed} value The value to get errors for (defaults to the current field value)
59762      * @return {Array} All validation errors for this field
59763      */
59764     getErrors: function(value) {
59765         var errors = Ext.form.DateField.superclass.getErrors.apply(this, arguments);
59766         
59767         value = this.formatDate(value || this.processValue(this.getRawValue()));
59768         
59769         if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
59770              return errors;
59771         }
59772         
59773         var svalue = value;
59774         value = this.parseDate(value);
59775         if (!value) {
59776             errors.push(String.format(this.invalidText, svalue, this.format));
59777             return errors;
59778         }
59779         
59780         var time = value.getTime();
59781         if (this.minValue && time < this.minValue.getTime()) {
59782             errors.push(String.format(this.minText, this.formatDate(this.minValue)));
59783         }
59784         
59785         if (this.maxValue && time > this.maxValue.getTime()) {
59786             errors.push(String.format(this.maxText, this.formatDate(this.maxValue)));
59787         }
59788         
59789         if (this.disabledDays) {
59790             var day = value.getDay();
59791             
59792             for(var i = 0; i < this.disabledDays.length; i++) {
59793                 if (day === this.disabledDays[i]) {
59794                     errors.push(this.disabledDaysText);
59795                     break;
59796                 }
59797             }
59798         }
59799         
59800         var fvalue = this.formatDate(value);
59801         if (this.disabledDatesRE && this.disabledDatesRE.test(fvalue)) {
59802             errors.push(String.format(this.disabledDatesText, fvalue));
59803         }
59804         
59805         return errors;
59806     },
59807
59808     // private
59809     // Provides logic to override the default TriggerField.validateBlur which just returns true
59810     validateBlur : function(){
59811         return !this.menu || !this.menu.isVisible();
59812     },
59813
59814     /**
59815      * Returns the current date value of the date field.
59816      * @return {Date} The date value
59817      */
59818     getValue : function(){
59819         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
59820     },
59821
59822     /**
59823      * Sets the value of the date field.  You can pass a date object or any string that can be
59824      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
59825      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
59826      * <br />Usage:
59827      * <pre><code>
59828 //All of these calls set the same date value (May 4, 2006)
59829
59830 //Pass a date object:
59831 var dt = new Date('5/4/2006');
59832 dateField.setValue(dt);
59833
59834 //Pass a date string (default format):
59835 dateField.setValue('05/04/2006');
59836
59837 //Pass a date string (custom format):
59838 dateField.format = 'Y-m-d';
59839 dateField.setValue('2006-05-04');
59840 </code></pre>
59841      * @param {String/Date} date The date or valid date string
59842      * @return {Ext.form.Field} this
59843      */
59844     setValue : function(date){
59845         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
59846     },
59847
59848     // private
59849     parseDate : function(value) {
59850         if(!value || Ext.isDate(value)){
59851             return value;
59852         }
59853
59854         var v = this.safeParse(value, this.format),
59855             af = this.altFormats,
59856             afa = this.altFormatsArray;
59857
59858         if (!v && af) {
59859             afa = afa || af.split("|");
59860
59861             for (var i = 0, len = afa.length; i < len && !v; i++) {
59862                 v = this.safeParse(value, afa[i]);
59863             }
59864         }
59865         return v;
59866     },
59867
59868     // private
59869     onDestroy : function(){
59870         Ext.destroy(this.menu, this.keyNav);
59871         Ext.form.DateField.superclass.onDestroy.call(this);
59872     },
59873
59874     // private
59875     formatDate : function(date){
59876         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
59877     },
59878
59879     /**
59880      * @method onTriggerClick
59881      * @hide
59882      */
59883     // private
59884     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
59885     onTriggerClick : function(){
59886         if(this.disabled){
59887             return;
59888         }
59889         if(this.menu == null){
59890             this.menu = new Ext.menu.DateMenu({
59891                 hideOnClick: false,
59892                 focusOnSelect: false
59893             });
59894         }
59895         this.onFocus();
59896         Ext.apply(this.menu.picker,  {
59897             minDate : this.minValue,
59898             maxDate : this.maxValue,
59899             disabledDatesRE : this.disabledDatesRE,
59900             disabledDatesText : this.disabledDatesText,
59901             disabledDays : this.disabledDays,
59902             disabledDaysText : this.disabledDaysText,
59903             format : this.format,
59904             showToday : this.showToday,
59905             minText : String.format(this.minText, this.formatDate(this.minValue)),
59906             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
59907         });
59908         this.menu.picker.setValue(this.getValue() || new Date());
59909         this.menu.show(this.el, "tl-bl?");
59910         this.menuEvents('on');
59911     },
59912
59913     //private
59914     menuEvents: function(method){
59915         this.menu[method]('select', this.onSelect, this);
59916         this.menu[method]('hide', this.onMenuHide, this);
59917         this.menu[method]('show', this.onFocus, this);
59918     },
59919
59920     onSelect: function(m, d){
59921         this.setValue(d);
59922         this.fireEvent('select', this, d);
59923         this.menu.hide();
59924     },
59925
59926     onMenuHide: function(){
59927         this.focus(false, 60);
59928         this.menuEvents('un');
59929     },
59930
59931     // private
59932     beforeBlur : function(){
59933         var v = this.parseDate(this.getRawValue());
59934         if(v){
59935             this.setValue(v);
59936         }
59937     }
59938
59939     /**
59940      * @cfg {Boolean} grow @hide
59941      */
59942     /**
59943      * @cfg {Number} growMin @hide
59944      */
59945     /**
59946      * @cfg {Number} growMax @hide
59947      */
59948     /**
59949      * @hide
59950      * @method autoSize
59951      */
59952 });
59953 Ext.reg('datefield', Ext.form.DateField);/**
59954  * @class Ext.form.DisplayField
59955  * @extends Ext.form.Field
59956  * A display-only text field which is not validated and not submitted.
59957  * @constructor
59958  * Creates a new DisplayField.
59959  * @param {Object} config Configuration options
59960  * @xtype displayfield
59961  */
59962 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {
59963     validationEvent : false,
59964     validateOnBlur : false,
59965     defaultAutoCreate : {tag: "div"},
59966     /**
59967      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)
59968      */
59969     fieldClass : "x-form-display-field",
59970     /**
59971      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to
59972      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than
59973      * rendering them as string literals per the default logic.
59974      */
59975     htmlEncode: false,
59976
59977     // private
59978     initEvents : Ext.emptyFn,
59979
59980     isValid : function(){
59981         return true;
59982     },
59983
59984     validate : function(){
59985         return true;
59986     },
59987
59988     getRawValue : function(){
59989         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');
59990         if(v === this.emptyText){
59991             v = '';
59992         }
59993         if(this.htmlEncode){
59994             v = Ext.util.Format.htmlDecode(v);
59995         }
59996         return v;
59997     },
59998
59999     getValue : function(){
60000         return this.getRawValue();
60001     },
60002     
60003     getName: function() {
60004         return this.name;
60005     },
60006
60007     setRawValue : function(v){
60008         if(this.htmlEncode){
60009             v = Ext.util.Format.htmlEncode(v);
60010         }
60011         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);
60012     },
60013
60014     setValue : function(v){
60015         this.setRawValue(v);
60016         return this;
60017     }
60018     /** 
60019      * @cfg {String} inputType 
60020      * @hide
60021      */
60022     /** 
60023      * @cfg {Boolean} disabled 
60024      * @hide
60025      */
60026     /** 
60027      * @cfg {Boolean} readOnly 
60028      * @hide
60029      */
60030     /** 
60031      * @cfg {Boolean} validateOnBlur 
60032      * @hide
60033      */
60034     /** 
60035      * @cfg {Number} validationDelay 
60036      * @hide
60037      */
60038     /** 
60039      * @cfg {String/Boolean} validationEvent 
60040      * @hide
60041      */
60042 });
60043
60044 Ext.reg('displayfield', Ext.form.DisplayField);
60045 /**
60046  * @class Ext.form.ComboBox
60047  * @extends Ext.form.TriggerField
60048  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
60049  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is
60050  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
60051  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
60052  * which is named according to the {@link #name}.</p>
60053  * <p><b><u>Events</u></b></p>
60054  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
60055 var cb = new Ext.form.ComboBox({
60056     // all of your config options
60057     listeners:{
60058          scope: yourScope,
60059          'select': yourFunction
60060     }
60061 });
60062
60063 // Alternatively, you can assign events after the object is created:
60064 var cb = new Ext.form.ComboBox(yourOptions);
60065 cb.on('select', yourFunction, yourScope);
60066  * </code></pre></p>
60067  *
60068  * <p><b><u>ComboBox in Grid</u></b></p>
60069  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
60070  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement
60071  * a reusable render, for example:<pre><code>
60072 // create reusable renderer
60073 Ext.util.Format.comboRenderer = function(combo){
60074     return function(value){
60075         var record = combo.findRecord(combo.{@link #valueField}, value);
60076         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
60077     }
60078 }
60079
60080 // create the combo instance
60081 var combo = new Ext.form.ComboBox({
60082     {@link #typeAhead}: true,
60083     {@link #triggerAction}: 'all',
60084     {@link #lazyRender}:true,
60085     {@link #mode}: 'local',
60086     {@link #store}: new Ext.data.ArrayStore({
60087         id: 0,
60088         fields: [
60089             'myId',
60090             'displayText'
60091         ],
60092         data: [[1, 'item1'], [2, 'item2']]
60093     }),
60094     {@link #valueField}: 'myId',
60095     {@link #displayField}: 'displayText'
60096 });
60097
60098 // snippet of column model used within grid
60099 var cm = new Ext.grid.ColumnModel([{
60100        ...
60101     },{
60102        header: "Some Header",
60103        dataIndex: 'whatever',
60104        width: 130,
60105        editor: combo, // specify reference to combo instance
60106        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
60107     },
60108     ...
60109 ]);
60110  * </code></pre></p>
60111  *
60112  * <p><b><u>Filtering</u></b></p>
60113  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
60114  * store manually see <tt>{@link #lastQuery}</tt>.</p>
60115  * @constructor
60116  * Create a new ComboBox.
60117  * @param {Object} config Configuration options
60118  * @xtype combo
60119  */
60120 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
60121     /**
60122      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
60123      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
60124      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
60125      */
60126     /**
60127      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
60128      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
60129      * defaults to <tt>false</tt>).
60130      */
60131     /**
60132      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
60133      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
60134      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
60135      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
60136      */
60137     /**
60138      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
60139      * Acceptable values for this property are:
60140      * <div class="mdetail-params"><ul>
60141      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
60142      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
60143      * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
60144      * <div class="mdetail-params"><ul>
60145      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
60146      * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
60147      * {@link #valueField} and {@link #displayField})</div></li>
60148      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
60149      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
60150      * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
60151      * </div></li></ul></div></li></ul></div>
60152      * <p>See also <tt>{@link #mode}</tt>.</p>
60153      */
60154     /**
60155      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
60156      * the dropdown list (defaults to undefined, with no header element)
60157      */
60158
60159     // private
60160     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
60161     /**
60162      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
60163      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}
60164      */
60165     /**
60166      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
60167      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
60168      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
60169      * the store configuration}).
60170      * <p>See also <tt>{@link #valueField}</tt>.</p>
60171      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
60172      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
60173      * active.</p>
60174      */
60175     /**
60176      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
60177      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
60178      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
60179      * the store configuration}).
60180      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
60181      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
60182      */
60183     /**
60184      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
60185      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
60186      * post during a form submission.  See also {@link #valueField}.
60187      * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
60188      * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
60189      * no two DOM nodes should share the same id.  So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
60190      * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
60191      */
60192     /**
60193      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
60194      * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>).  The <tt>hiddenId</tt>
60195      * and combo {@link Ext.Component#id id} should be different, since no two DOM
60196      * nodes should share the same id.
60197      */
60198     /**
60199      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
60200      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
60201      * <tt>{@link Ext.form.Field#value value}</tt>.
60202      */
60203     /**
60204      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
60205      * applied the dropdown list element (defaults to '').
60206      */
60207     listClass : '',
60208     /**
60209      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
60210      * (defaults to <tt>'x-combo-selected'</tt>)
60211      */
60212     selectedClass : 'x-combo-selected',
60213     /**
60214      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
60215      * (defaults to '')
60216      */
60217     listEmptyText: '',
60218     /**
60219      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
60220      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
60221      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
60222      */
60223     triggerClass : 'x-form-arrow-trigger',
60224     /**
60225      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
60226      * 4-way shadow, and <tt>"drop"</tt> for bottom-right
60227      */
60228     shadow : 'sides',
60229     /**
60230      * @cfg {String/Array} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
60231      * on supported anchor positions and offsets. To specify x/y offsets as well, this value
60232      * may be specified as an Array of <tt>{@link Ext.Element#alignTo}</tt> method arguments.</p>
60233      * <pre><code>[ 'tl-bl?', [6,0] ]</code></pre>(defaults to <tt>'tl-bl?'</tt>)
60234      */
60235     listAlign : 'tl-bl?',
60236     /**
60237      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
60238      * (defaults to <tt>300</tt>)
60239      */
60240     maxHeight : 300,
60241     /**
60242      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
60243      * distance to the viewport edges (defaults to <tt>90</tt>)
60244      */
60245     minHeight : 90,
60246     /**
60247      * @cfg {String} triggerAction The action to execute when the trigger is clicked.
60248      * <div class="mdetail-params"><ul>
60249      * <li><b><tt>'query'</tt></b> : <b>Default</b>
60250      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
60251      * <li><b><tt>'all'</tt></b> :
60252      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
60253      * </ul></div>
60254      * <p>See also <code>{@link #queryParam}</code>.</p>
60255      */
60256     triggerAction : 'query',
60257     /**
60258      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
60259      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
60260      * <tt>{@link #mode} = 'local'</tt>, does not apply if
60261      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
60262      */
60263     minChars : 4,
60264     /**
60265      * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
60266      * to <tt>true</tt>).  A false value would require a manual selection from the dropdown list to set the components value
60267      * unless the value of ({@link #typeAheadDelay}) were true.
60268      */
60269     autoSelect : true,
60270     /**
60271      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
60272      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
60273      * to <tt>false</tt>)
60274      */
60275     typeAhead : false,
60276     /**
60277      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
60278      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
60279      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
60280      */
60281     queryDelay : 500,
60282     /**
60283      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
60284      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
60285      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
60286      * (defaults to <tt>0</tt>).
60287      */
60288     pageSize : 0,
60289     /**
60290      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
60291      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
60292      * <tt>false</tt>).
60293      */
60294     selectOnFocus : false,
60295     /**
60296      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
60297      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
60298      */
60299     queryParam : 'query',
60300     /**
60301      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
60302      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
60303      */
60304     loadingText : 'Loading...',
60305     /**
60306      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
60307      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
60308      * Defaults to <tt>false</tt>.
60309      */
60310     resizable : false,
60311     /**
60312      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
60313      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
60314      */
60315     handleHeight : 8,
60316     /**
60317      * @cfg {String} allQuery The text query to send to the server to return all records for the list
60318      * with no filtering (defaults to '')
60319      */
60320     allQuery: '',
60321     /**
60322      * @cfg {String} mode Acceptable values are:
60323      * <div class="mdetail-params"><ul>
60324      * <li><b><tt>'remote'</tt></b> : <b>Default</b>
60325      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
60326      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
60327      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
60328      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
60329      * <li><b><tt>'local'</tt></b> :
60330      * <p class="sub-desc">ComboBox loads local data</p>
60331      * <pre><code>
60332 var combo = new Ext.form.ComboBox({
60333     renderTo: document.body,
60334     mode: 'local',
60335     store: new Ext.data.ArrayStore({
60336         id: 0,
60337         fields: [
60338             'myId',  // numeric value is the key
60339             'displayText'
60340         ],
60341         data: [[1, 'item1'], [2, 'item2']]  // data is local
60342     }),
60343     valueField: 'myId',
60344     displayField: 'displayText',
60345     triggerAction: 'all'
60346 });
60347      * </code></pre></li>
60348      * </ul></div>
60349      */
60350     mode: 'remote',
60351     /**
60352      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
60353      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
60354      */
60355     minListWidth : 70,
60356     /**
60357      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
60358      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
60359      */
60360     forceSelection : false,
60361     /**
60362      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
60363      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
60364      */
60365     typeAheadDelay : 250,
60366     /**
60367      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
60368      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
60369      * default text is used, it means there is no value set and no validation will occur on this field.
60370      */
60371
60372     /**
60373      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
60374      * (defaults to <tt>true</tt>)
60375      */
60376     lazyInit : true,
60377
60378     /**
60379      * @cfg {Boolean} clearFilterOnReset <tt>true</tt> to clear any filters on the store (when in local mode) when reset is called
60380      * (defaults to <tt>true</tt>)
60381      */
60382     clearFilterOnReset : true,
60383
60384     /**
60385      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
60386      * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
60387      * Defaults to <tt>undefined</tt>.
60388      */
60389     submitValue: undefined,
60390
60391     /**
60392      * The value of the match string used to filter the store. Delete this property to force a requery.
60393      * Example use:
60394      * <pre><code>
60395 var combo = new Ext.form.ComboBox({
60396     ...
60397     mode: 'remote',
60398     ...
60399     listeners: {
60400         // delete the previous query in the beforequery event or set
60401         // combo.lastQuery = null (this will reload the store the next time it expands)
60402         beforequery: function(qe){
60403             delete qe.combo.lastQuery;
60404         }
60405     }
60406 });
60407      * </code></pre>
60408      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
60409      * configure the combo with <tt>lastQuery=''</tt>. Example use:
60410      * <pre><code>
60411 var combo = new Ext.form.ComboBox({
60412     ...
60413     mode: 'local',
60414     triggerAction: 'all',
60415     lastQuery: ''
60416 });
60417      * </code></pre>
60418      * @property lastQuery
60419      * @type String
60420      */
60421
60422     // private
60423     initComponent : function(){
60424         Ext.form.ComboBox.superclass.initComponent.call(this);
60425         this.addEvents(
60426             /**
60427              * @event expand
60428              * Fires when the dropdown list is expanded
60429              * @param {Ext.form.ComboBox} combo This combo box
60430              */
60431             'expand',
60432             /**
60433              * @event collapse
60434              * Fires when the dropdown list is collapsed
60435              * @param {Ext.form.ComboBox} combo This combo box
60436              */
60437             'collapse',
60438
60439             /**
60440              * @event beforeselect
60441              * Fires before a list item is selected. Return false to cancel the selection.
60442              * @param {Ext.form.ComboBox} combo This combo box
60443              * @param {Ext.data.Record} record The data record returned from the underlying store
60444              * @param {Number} index The index of the selected item in the dropdown list
60445              */
60446             'beforeselect',
60447             /**
60448              * @event select
60449              * Fires when a list item is selected
60450              * @param {Ext.form.ComboBox} combo This combo box
60451              * @param {Ext.data.Record} record The data record returned from the underlying store
60452              * @param {Number} index The index of the selected item in the dropdown list
60453              */
60454             'select',
60455             /**
60456              * @event beforequery
60457              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
60458              * cancel property to true.
60459              * @param {Object} queryEvent An object that has these properties:<ul>
60460              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
60461              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
60462              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
60463              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
60464              * </ul>
60465              */
60466             'beforequery'
60467         );
60468         if(this.transform){
60469             var s = Ext.getDom(this.transform);
60470             if(!this.hiddenName){
60471                 this.hiddenName = s.name;
60472             }
60473             if(!this.store){
60474                 this.mode = 'local';
60475                 var d = [], opts = s.options;
60476                 for(var i = 0, len = opts.length;i < len; i++){
60477                     var o = opts[i],
60478                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
60479                     if(o.selected && Ext.isEmpty(this.value, true)) {
60480                         this.value = value;
60481                     }
60482                     d.push([value, o.text]);
60483                 }
60484                 this.store = new Ext.data.ArrayStore({
60485                     'id': 0,
60486                     fields: ['value', 'text'],
60487                     data : d,
60488                     autoDestroy: true
60489                 });
60490                 this.valueField = 'value';
60491                 this.displayField = 'text';
60492             }
60493             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
60494             if(!this.lazyRender){
60495                 this.target = true;
60496                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
60497                 this.render(this.el.parentNode, s);
60498             }
60499             Ext.removeNode(s);
60500         }
60501         //auto-configure store from local array data
60502         else if(this.store){
60503             this.store = Ext.StoreMgr.lookup(this.store);
60504             if(this.store.autoCreated){
60505                 this.displayField = this.valueField = 'field1';
60506                 if(!this.store.expandData){
60507                     this.displayField = 'field2';
60508                 }
60509                 this.mode = 'local';
60510             }
60511         }
60512
60513         this.selectedIndex = -1;
60514         if(this.mode == 'local'){
60515             if(!Ext.isDefined(this.initialConfig.queryDelay)){
60516                 this.queryDelay = 10;
60517             }
60518             if(!Ext.isDefined(this.initialConfig.minChars)){
60519                 this.minChars = 0;
60520             }
60521         }
60522     },
60523
60524     // private
60525     onRender : function(ct, position){
60526         if(this.hiddenName && !Ext.isDefined(this.submitValue)){
60527             this.submitValue = false;
60528         }
60529         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
60530         if(this.hiddenName){
60531             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
60532                     id: (this.hiddenId||this.hiddenName)}, 'before', true);
60533
60534         }
60535         if(Ext.isGecko){
60536             this.el.dom.setAttribute('autocomplete', 'off');
60537         }
60538
60539         if(!this.lazyInit){
60540             this.initList();
60541         }else{
60542             this.on('focus', this.initList, this, {single: true});
60543         }
60544     },
60545
60546     // private
60547     initValue : function(){
60548         Ext.form.ComboBox.superclass.initValue.call(this);
60549         if(this.hiddenField){
60550             this.hiddenField.value =
60551                 Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, '');
60552         }
60553     },
60554
60555     getParentZIndex : function(){
60556         var zindex;
60557         if (this.ownerCt){
60558             this.findParentBy(function(ct){
60559                 zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
60560                 return !!zindex;
60561             });
60562         }
60563         return zindex;
60564     },
60565
60566     // private
60567     initList : function(){
60568         if(!this.list){
60569             var cls = 'x-combo-list',
60570                 listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
60571                 zindex = parseInt(Ext.fly(listParent).getStyle('z-index'), 10);
60572
60573             if (!zindex) {
60574                 zindex = this.getParentZIndex();
60575             }
60576
60577             this.list = new Ext.Layer({
60578                 parentEl: listParent,
60579                 shadow: this.shadow,
60580                 cls: [cls, this.listClass].join(' '),
60581                 constrain:false,
60582                 zindex: (zindex || 12000) + 5
60583             });
60584
60585             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
60586             this.list.setSize(lw, 0);
60587             this.list.swallowEvent('mousewheel');
60588             this.assetHeight = 0;
60589             if(this.syncFont !== false){
60590                 this.list.setStyle('font-size', this.el.getStyle('font-size'));
60591             }
60592             if(this.title){
60593                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
60594                 this.assetHeight += this.header.getHeight();
60595             }
60596
60597             this.innerList = this.list.createChild({cls:cls+'-inner'});
60598             this.mon(this.innerList, 'mouseover', this.onViewOver, this);
60599             this.mon(this.innerList, 'mousemove', this.onViewMove, this);
60600             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
60601
60602             if(this.pageSize){
60603                 this.footer = this.list.createChild({cls:cls+'-ft'});
60604                 this.pageTb = new Ext.PagingToolbar({
60605                     store: this.store,
60606                     pageSize: this.pageSize,
60607                     renderTo:this.footer
60608                 });
60609                 this.assetHeight += this.footer.getHeight();
60610             }
60611
60612             if(!this.tpl){
60613                 /**
60614                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
60615                 * use to display each item in the dropdown list. The dropdown list is displayed in a
60616                 * DataView. See {@link #view}.</p>
60617                 * <p>The default template string is:</p><pre><code>
60618                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'
60619                 * </code></pre>
60620                 * <p>Override the default value to create custom UI layouts for items in the list.
60621                 * For example:</p><pre><code>
60622                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'
60623                 * </code></pre>
60624                 * <p>The template <b>must</b> contain one or more substitution parameters using field
60625                 * names from the Combo's</b> {@link #store Store}. In the example above an
60626                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
60627                 * <p>To preserve the default visual look of list items, add the CSS class name
60628                 * <pre>x-combo-list-item</pre> to the template's container element.</p>
60629                 * <p>Also see {@link #itemSelector} for additional details.</p>
60630                 */
60631                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
60632                 /**
60633                  * @cfg {String} itemSelector
60634                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
60635                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
60636                  * display will be working with.</p>
60637                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
60638                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
60639                  * to dropdown list items</b>
60640                  */
60641             }
60642
60643             /**
60644             * The {@link Ext.DataView DataView} used to display the ComboBox's options.
60645             * @type Ext.DataView
60646             */
60647             this.view = new Ext.DataView({
60648                 applyTo: this.innerList,
60649                 tpl: this.tpl,
60650                 singleSelect: true,
60651                 selectedClass: this.selectedClass,
60652                 itemSelector: this.itemSelector || '.' + cls + '-item',
60653                 emptyText: this.listEmptyText,
60654                 deferEmptyText: false
60655             });
60656
60657             this.mon(this.view, {
60658                 containerclick : this.onViewClick,
60659                 click : this.onViewClick,
60660                 scope :this
60661             });
60662
60663             this.bindStore(this.store, true);
60664
60665             if(this.resizable){
60666                 this.resizer = new Ext.Resizable(this.list,  {
60667                    pinned:true, handles:'se'
60668                 });
60669                 this.mon(this.resizer, 'resize', function(r, w, h){
60670                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
60671                     this.listWidth = w;
60672                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
60673                     this.restrictHeight();
60674                 }, this);
60675
60676                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
60677             }
60678         }
60679     },
60680
60681     /**
60682      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
60683      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
60684      * to a different Element. An example might be rendering the list inside a Menu so that clicking
60685      * the list does not hide the Menu:<pre><code>
60686 var store = new Ext.data.ArrayStore({
60687     autoDestroy: true,
60688     fields: ['initials', 'fullname'],
60689     data : [
60690         ['FF', 'Fred Flintstone'],
60691         ['BR', 'Barney Rubble']
60692     ]
60693 });
60694
60695 var combo = new Ext.form.ComboBox({
60696     store: store,
60697     displayField: 'fullname',
60698     emptyText: 'Select a name...',
60699     forceSelection: true,
60700     getListParent: function() {
60701         return this.el.up('.x-menu');
60702     },
60703     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
60704     mode: 'local',
60705     selectOnFocus: true,
60706     triggerAction: 'all',
60707     typeAhead: true,
60708     width: 135
60709 });
60710
60711 var menu = new Ext.menu.Menu({
60712     id: 'mainMenu',
60713     items: [
60714         combo // A Field in a Menu
60715     ]
60716 });
60717 </code></pre>
60718      */
60719     getListParent : function() {
60720         return document.body;
60721     },
60722
60723     /**
60724      * Returns the store associated with this combo.
60725      * @return {Ext.data.Store} The store
60726      */
60727     getStore : function(){
60728         return this.store;
60729     },
60730
60731     // private
60732     bindStore : function(store, initial){
60733         if(this.store && !initial){
60734             if(this.store !== store && this.store.autoDestroy){
60735                 this.store.destroy();
60736             }else{
60737                 this.store.un('beforeload', this.onBeforeLoad, this);
60738                 this.store.un('load', this.onLoad, this);
60739                 this.store.un('exception', this.collapse, this);
60740             }
60741             if(!store){
60742                 this.store = null;
60743                 if(this.view){
60744                     this.view.bindStore(null);
60745                 }
60746                 if(this.pageTb){
60747                     this.pageTb.bindStore(null);
60748                 }
60749             }
60750         }
60751         if(store){
60752             if(!initial) {
60753                 this.lastQuery = null;
60754                 if(this.pageTb) {
60755                     this.pageTb.bindStore(store);
60756                 }
60757             }
60758
60759             this.store = Ext.StoreMgr.lookup(store);
60760             this.store.on({
60761                 scope: this,
60762                 beforeload: this.onBeforeLoad,
60763                 load: this.onLoad,
60764                 exception: this.collapse
60765             });
60766
60767             if(this.view){
60768                 this.view.bindStore(store);
60769             }
60770         }
60771     },
60772
60773     reset : function(){
60774         Ext.form.ComboBox.superclass.reset.call(this);
60775         if(this.clearFilterOnReset && this.mode == 'local'){
60776             this.store.clearFilter();
60777         }
60778     },
60779
60780     // private
60781     initEvents : function(){
60782         Ext.form.ComboBox.superclass.initEvents.call(this);
60783
60784
60785         this.keyNav = new Ext.KeyNav(this.el, {
60786             "up" : function(e){
60787                 this.inKeyMode = true;
60788                 this.selectPrev();
60789             },
60790
60791             "down" : function(e){
60792                 if(!this.isExpanded()){
60793                     this.onTriggerClick();
60794                 }else{
60795                     this.inKeyMode = true;
60796                     this.selectNext();
60797                 }
60798             },
60799
60800             "enter" : function(e){
60801                 this.onViewClick();
60802             },
60803
60804             "esc" : function(e){
60805                 this.collapse();
60806             },
60807
60808             "tab" : function(e){
60809                 if (this.forceSelection === true) {
60810                     this.collapse();
60811                 } else {
60812                     this.onViewClick(false);
60813                 }
60814                 return true;
60815             },
60816
60817             scope : this,
60818
60819             doRelay : function(e, h, hname){
60820                 if(hname == 'down' || this.scope.isExpanded()){
60821                     // this MUST be called before ComboBox#fireKey()
60822                     var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
60823                     if(!Ext.isIE && Ext.EventManager.useKeydown){
60824                         // call Combo#fireKey() for browsers which use keydown event (except IE)
60825                         this.scope.fireKey(e);
60826                     }
60827                     return relay;
60828                 }
60829                 return true;
60830             },
60831
60832             forceKeyDown : true,
60833             defaultEventAction: 'stopEvent'
60834         });
60835         this.queryDelay = Math.max(this.queryDelay || 10,
60836                 this.mode == 'local' ? 10 : 250);
60837         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
60838         if(this.typeAhead){
60839             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
60840         }
60841         if(!this.enableKeyEvents){
60842             this.mon(this.el, 'keyup', this.onKeyUp, this);
60843         }
60844     },
60845
60846
60847     // private
60848     onDestroy : function(){
60849         if (this.dqTask){
60850             this.dqTask.cancel();
60851             this.dqTask = null;
60852         }
60853         this.bindStore(null);
60854         Ext.destroy(
60855             this.resizer,
60856             this.view,
60857             this.pageTb,
60858             this.list
60859         );
60860         Ext.destroyMembers(this, 'hiddenField');
60861         Ext.form.ComboBox.superclass.onDestroy.call(this);
60862     },
60863
60864     // private
60865     fireKey : function(e){
60866         if (!this.isExpanded()) {
60867             Ext.form.ComboBox.superclass.fireKey.call(this, e);
60868         }
60869     },
60870
60871     // private
60872     onResize : function(w, h){
60873         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
60874         if(!isNaN(w) && this.isVisible() && this.list){
60875             this.doResize(w);
60876         }else{
60877             this.bufferSize = w;
60878         }
60879     },
60880
60881     doResize: function(w){
60882         if(!Ext.isDefined(this.listWidth)){
60883             var lw = Math.max(w, this.minListWidth);
60884             this.list.setWidth(lw);
60885             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
60886         }
60887     },
60888
60889     // private
60890     onEnable : function(){
60891         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
60892         if(this.hiddenField){
60893             this.hiddenField.disabled = false;
60894         }
60895     },
60896
60897     // private
60898     onDisable : function(){
60899         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
60900         if(this.hiddenField){
60901             this.hiddenField.disabled = true;
60902         }
60903     },
60904
60905     // private
60906     onBeforeLoad : function(){
60907         if(!this.hasFocus){
60908             return;
60909         }
60910         this.innerList.update(this.loadingText ?
60911                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
60912         this.restrictHeight();
60913         this.selectedIndex = -1;
60914     },
60915
60916     // private
60917     onLoad : function(){
60918         if(!this.hasFocus){
60919             return;
60920         }
60921         if(this.store.getCount() > 0 || this.listEmptyText){
60922             this.expand();
60923             this.restrictHeight();
60924             if(this.lastQuery == this.allQuery){
60925                 if(this.editable){
60926                     this.el.dom.select();
60927                 }
60928
60929                 if(this.autoSelect !== false && !this.selectByValue(this.value, true)){
60930                     this.select(0, true);
60931                 }
60932             }else{
60933                 if(this.autoSelect !== false){
60934                     this.selectNext();
60935                 }
60936                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
60937                     this.taTask.delay(this.typeAheadDelay);
60938                 }
60939             }
60940         }else{
60941             this.collapse();
60942         }
60943
60944     },
60945
60946     // private
60947     onTypeAhead : function(){
60948         if(this.store.getCount() > 0){
60949             var r = this.store.getAt(0);
60950             var newValue = r.data[this.displayField];
60951             var len = newValue.length;
60952             var selStart = this.getRawValue().length;
60953             if(selStart != len){
60954                 this.setRawValue(newValue);
60955                 this.selectText(selStart, newValue.length);
60956             }
60957         }
60958     },
60959
60960     // private
60961     assertValue  : function(){
60962         var val = this.getRawValue(),
60963             rec = this.findRecord(this.displayField, val);
60964
60965         if(!rec && this.forceSelection){
60966             if(val.length > 0 && val != this.emptyText){
60967                 this.el.dom.value = Ext.value(this.lastSelectionText, '');
60968                 this.applyEmptyText();
60969             }else{
60970                 this.clearValue();
60971             }
60972         }else{
60973             if(rec){
60974                 // onSelect may have already set the value and by doing so
60975                 // set the display field properly.  Let's not wipe out the
60976                 // valueField here by just sending the displayField.
60977                 if (val == rec.get(this.displayField) && this.value == rec.get(this.valueField)){
60978                     return;
60979                 }
60980                 val = rec.get(this.valueField || this.displayField);
60981             }
60982             this.setValue(val);
60983         }
60984     },
60985
60986     // private
60987     onSelect : function(record, index){
60988         if(this.fireEvent('beforeselect', this, record, index) !== false){
60989             this.setValue(record.data[this.valueField || this.displayField]);
60990             this.collapse();
60991             this.fireEvent('select', this, record, index);
60992         }
60993     },
60994
60995     // inherit docs
60996     getName: function(){
60997         var hf = this.hiddenField;
60998         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
60999     },
61000
61001     /**
61002      * Returns the currently selected field value or empty string if no value is set.
61003      * @return {String} value The selected value
61004      */
61005     getValue : function(){
61006         if(this.valueField){
61007             return Ext.isDefined(this.value) ? this.value : '';
61008         }else{
61009             return Ext.form.ComboBox.superclass.getValue.call(this);
61010         }
61011     },
61012
61013     /**
61014      * Clears any text/value currently set in the field
61015      */
61016     clearValue : function(){
61017         if(this.hiddenField){
61018             this.hiddenField.value = '';
61019         }
61020         this.setRawValue('');
61021         this.lastSelectionText = '';
61022         this.applyEmptyText();
61023         this.value = '';
61024     },
61025
61026     /**
61027      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
61028      * will be displayed in the field.  If the value does not match the data value of an existing item,
61029      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
61030      * Otherwise the field will be blank (although the value will still be set).
61031      * @param {String} value The value to match
61032      * @return {Ext.form.Field} this
61033      */
61034     setValue : function(v){
61035         var text = v;
61036         if(this.valueField){
61037             var r = this.findRecord(this.valueField, v);
61038             if(r){
61039                 text = r.data[this.displayField];
61040             }else if(Ext.isDefined(this.valueNotFoundText)){
61041                 text = this.valueNotFoundText;
61042             }
61043         }
61044         this.lastSelectionText = text;
61045         if(this.hiddenField){
61046             this.hiddenField.value = Ext.value(v, '');
61047         }
61048         Ext.form.ComboBox.superclass.setValue.call(this, text);
61049         this.value = v;
61050         return this;
61051     },
61052
61053     // private
61054     findRecord : function(prop, value){
61055         var record;
61056         if(this.store.getCount() > 0){
61057             this.store.each(function(r){
61058                 if(r.data[prop] == value){
61059                     record = r;
61060                     return false;
61061                 }
61062             });
61063         }
61064         return record;
61065     },
61066
61067     // private
61068     onViewMove : function(e, t){
61069         this.inKeyMode = false;
61070     },
61071
61072     // private
61073     onViewOver : function(e, t){
61074         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
61075             return;
61076         }
61077         var item = this.view.findItemFromChild(t);
61078         if(item){
61079             var index = this.view.indexOf(item);
61080             this.select(index, false);
61081         }
61082     },
61083
61084     // private
61085     onViewClick : function(doFocus){
61086         var index = this.view.getSelectedIndexes()[0],
61087             s = this.store,
61088             r = s.getAt(index);
61089         if(r){
61090             this.onSelect(r, index);
61091         }else {
61092             this.collapse();
61093         }
61094         if(doFocus !== false){
61095             this.el.focus();
61096         }
61097     },
61098
61099
61100     // private
61101     restrictHeight : function(){
61102         this.innerList.dom.style.height = '';
61103         var inner = this.innerList.dom,
61104             pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
61105             h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
61106             ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
61107             hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
61108             space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
61109
61110         h = Math.min(h, space, this.maxHeight);
61111
61112         this.innerList.setHeight(h);
61113         this.list.beginUpdate();
61114         this.list.setHeight(h+pad);
61115         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
61116         this.list.endUpdate();
61117     },
61118
61119     /**
61120      * Returns true if the dropdown list is expanded, else false.
61121      */
61122     isExpanded : function(){
61123         return this.list && this.list.isVisible();
61124     },
61125
61126     /**
61127      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
61128      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
61129      * @param {String} value The data value of the item to select
61130      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
61131      * selected item if it is not currently in view (defaults to true)
61132      * @return {Boolean} True if the value matched an item in the list, else false
61133      */
61134     selectByValue : function(v, scrollIntoView){
61135         if(!Ext.isEmpty(v, true)){
61136             var r = this.findRecord(this.valueField || this.displayField, v);
61137             if(r){
61138                 this.select(this.store.indexOf(r), scrollIntoView);
61139                 return true;
61140             }
61141         }
61142         return false;
61143     },
61144
61145     /**
61146      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
61147      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
61148      * @param {Number} index The zero-based index of the list item to select
61149      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
61150      * selected item if it is not currently in view (defaults to true)
61151      */
61152     select : function(index, scrollIntoView){
61153         this.selectedIndex = index;
61154         this.view.select(index);
61155         if(scrollIntoView !== false){
61156             var el = this.view.getNode(index);
61157             if(el){
61158                 this.innerList.scrollChildIntoView(el, false);
61159             }
61160         }
61161
61162     },
61163
61164     // private
61165     selectNext : function(){
61166         var ct = this.store.getCount();
61167         if(ct > 0){
61168             if(this.selectedIndex == -1){
61169                 this.select(0);
61170             }else if(this.selectedIndex < ct-1){
61171                 this.select(this.selectedIndex+1);
61172             }
61173         }
61174     },
61175
61176     // private
61177     selectPrev : function(){
61178         var ct = this.store.getCount();
61179         if(ct > 0){
61180             if(this.selectedIndex == -1){
61181                 this.select(0);
61182             }else if(this.selectedIndex !== 0){
61183                 this.select(this.selectedIndex-1);
61184             }
61185         }
61186     },
61187
61188     // private
61189     onKeyUp : function(e){
61190         var k = e.getKey();
61191         if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
61192
61193             this.lastKey = k;
61194             this.dqTask.delay(this.queryDelay);
61195         }
61196         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
61197     },
61198
61199     // private
61200     validateBlur : function(){
61201         return !this.list || !this.list.isVisible();
61202     },
61203
61204     // private
61205     initQuery : function(){
61206         this.doQuery(this.getRawValue());
61207     },
61208
61209     // private
61210     beforeBlur : function(){
61211         this.assertValue();
61212     },
61213
61214     // private
61215     postBlur  : function(){
61216         Ext.form.ComboBox.superclass.postBlur.call(this);
61217         this.collapse();
61218         this.inKeyMode = false;
61219     },
61220
61221     /**
61222      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the
61223      * query allowing the query action to be canceled if needed.
61224      * @param {String} query The SQL query to execute
61225      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
61226      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
61227      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
61228      */
61229     doQuery : function(q, forceAll){
61230         q = Ext.isEmpty(q) ? '' : q;
61231         var qe = {
61232             query: q,
61233             forceAll: forceAll,
61234             combo: this,
61235             cancel:false
61236         };
61237         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
61238             return false;
61239         }
61240         q = qe.query;
61241         forceAll = qe.forceAll;
61242         if(forceAll === true || (q.length >= this.minChars)){
61243             if(this.lastQuery !== q){
61244                 this.lastQuery = q;
61245                 if(this.mode == 'local'){
61246                     this.selectedIndex = -1;
61247                     if(forceAll){
61248                         this.store.clearFilter();
61249                     }else{
61250                         this.store.filter(this.displayField, q);
61251                     }
61252                     this.onLoad();
61253                 }else{
61254                     this.store.baseParams[this.queryParam] = q;
61255                     this.store.load({
61256                         params: this.getParams(q)
61257                     });
61258                     this.expand();
61259                 }
61260             }else{
61261                 this.selectedIndex = -1;
61262                 this.onLoad();
61263             }
61264         }
61265     },
61266
61267     // private
61268     getParams : function(q){
61269         var p = {};
61270         //p[this.queryParam] = q;
61271         if(this.pageSize){
61272             p.start = 0;
61273             p.limit = this.pageSize;
61274         }
61275         return p;
61276     },
61277
61278     /**
61279      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
61280      */
61281     collapse : function(){
61282         if(!this.isExpanded()){
61283             return;
61284         }
61285         this.list.hide();
61286         Ext.getDoc().un('mousewheel', this.collapseIf, this);
61287         Ext.getDoc().un('mousedown', this.collapseIf, this);
61288         this.fireEvent('collapse', this);
61289     },
61290
61291     // private
61292     collapseIf : function(e){
61293         if(!this.isDestroyed && !e.within(this.wrap) && !e.within(this.list)){
61294             this.collapse();
61295         }
61296     },
61297
61298     /**
61299      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
61300      */
61301     expand : function(){
61302         if(this.isExpanded() || !this.hasFocus){
61303             return;
61304         }
61305
61306         if(this.title || this.pageSize){
61307             this.assetHeight = 0;
61308             if(this.title){
61309                 this.assetHeight += this.header.getHeight();
61310             }
61311             if(this.pageSize){
61312                 this.assetHeight += this.footer.getHeight();
61313             }
61314         }
61315
61316         if(this.bufferSize){
61317             this.doResize(this.bufferSize);
61318             delete this.bufferSize;
61319         }
61320         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
61321
61322         // zindex can change, re-check it and set it if necessary
61323         var listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
61324             zindex = parseInt(Ext.fly(listParent).getStyle('z-index') ,10);
61325         if (!zindex){
61326             zindex = this.getParentZIndex();
61327         }
61328         if (zindex) {
61329             this.list.setZIndex(zindex + 5);
61330         }
61331         this.list.show();
61332         if(Ext.isGecko2){
61333             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
61334         }
61335         this.mon(Ext.getDoc(), {
61336             scope: this,
61337             mousewheel: this.collapseIf,
61338             mousedown: this.collapseIf
61339         });
61340         this.fireEvent('expand', this);
61341     },
61342
61343     /**
61344      * @method onTriggerClick
61345      * @hide
61346      */
61347     // private
61348     // Implements the default empty TriggerField.onTriggerClick function
61349     onTriggerClick : function(){
61350         if(this.readOnly || this.disabled){
61351             return;
61352         }
61353         if(this.isExpanded()){
61354             this.collapse();
61355             this.el.focus();
61356         }else {
61357             this.onFocus({});
61358             if(this.triggerAction == 'all') {
61359                 this.doQuery(this.allQuery, true);
61360             } else {
61361                 this.doQuery(this.getRawValue());
61362             }
61363             this.el.focus();
61364         }
61365     }
61366
61367     /**
61368      * @hide
61369      * @method autoSize
61370      */
61371     /**
61372      * @cfg {Boolean} grow @hide
61373      */
61374     /**
61375      * @cfg {Number} growMin @hide
61376      */
61377     /**
61378      * @cfg {Number} growMax @hide
61379      */
61380
61381 });
61382 Ext.reg('combo', Ext.form.ComboBox);
61383 /**
61384  * @class Ext.form.Checkbox
61385  * @extends Ext.form.Field
61386  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
61387  * @constructor
61388  * Creates a new Checkbox
61389  * @param {Object} config Configuration options
61390  * @xtype checkbox
61391  */
61392 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
61393     /**
61394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
61395      */
61396     focusClass : undefined,
61397     /**
61398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
61399      */
61400     fieldClass : 'x-form-field',
61401     /**
61402      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
61403      */
61404     checked : false,
61405     /**
61406      * @cfg {String} boxLabel The text that appears beside the checkbox
61407      */
61408     boxLabel: '&#160;',
61409     /**
61410      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
61411      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
61412      */
61413     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
61414     /**
61415      * @cfg {String} boxLabel The text that appears beside the checkbox
61416      */
61417     /**
61418      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
61419      */
61420     /**
61421      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of
61422      * handling the check event). The handler is passed the following parameters:
61423      * <div class="mdetail-params"><ul>
61424      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
61425      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
61426      * </ul></div>
61427      */
61428     /**
61429      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
61430      * (defaults to this Checkbox).
61431      */
61432
61433     // private
61434     actionMode : 'wrap',
61435
61436         // private
61437     initComponent : function(){
61438         Ext.form.Checkbox.superclass.initComponent.call(this);
61439         this.addEvents(
61440             /**
61441              * @event check
61442              * Fires when the checkbox is checked or unchecked.
61443              * @param {Ext.form.Checkbox} this This checkbox
61444              * @param {Boolean} checked The new checked value
61445              */
61446             'check'
61447         );
61448     },
61449
61450     // private
61451     onResize : function(){
61452         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
61453         if(!this.boxLabel && !this.fieldLabel){
61454             this.el.alignTo(this.wrap, 'c-c');
61455         }
61456     },
61457
61458     // private
61459     initEvents : function(){
61460         Ext.form.Checkbox.superclass.initEvents.call(this);
61461         this.mon(this.el, {
61462             scope: this,
61463             click: this.onClick,
61464             change: this.onClick
61465         });
61466     },
61467
61468     /**
61469      * @hide
61470      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
61471      * @method
61472      */
61473     markInvalid : Ext.emptyFn,
61474     /**
61475      * @hide
61476      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
61477      * @method
61478      */
61479     clearInvalid : Ext.emptyFn,
61480
61481     // private
61482     onRender : function(ct, position){
61483         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
61484         if(this.inputValue !== undefined){
61485             this.el.dom.value = this.inputValue;
61486         }
61487         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
61488         if(this.boxLabel){
61489             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
61490         }
61491         if(this.checked){
61492             this.setValue(true);
61493         }else{
61494             this.checked = this.el.dom.checked;
61495         }
61496         // Need to repaint for IE, otherwise positioning is broken
61497         if(Ext.isIE){
61498             this.wrap.repaint();
61499         }
61500         this.resizeEl = this.positionEl = this.wrap;
61501     },
61502
61503     // private
61504     onDestroy : function(){
61505         Ext.destroy(this.wrap);
61506         Ext.form.Checkbox.superclass.onDestroy.call(this);
61507     },
61508
61509     // private
61510     initValue : function() {
61511         this.originalValue = this.getValue();
61512     },
61513
61514     /**
61515      * Returns the checked state of the checkbox.
61516      * @return {Boolean} True if checked, else false
61517      */
61518     getValue : function(){
61519         if(this.rendered){
61520             return this.el.dom.checked;
61521         }
61522         return this.checked;
61523     },
61524
61525         // private
61526     onClick : function(){
61527         if(this.el.dom.checked != this.checked){
61528             this.setValue(this.el.dom.checked);
61529         }
61530     },
61531
61532     /**
61533      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
61534      * <code>{@link #handler}</code> (if configured).
61535      * @param {Boolean/String} checked The following values will check the checkbox:
61536      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
61537      * @return {Ext.form.Field} this
61538      */
61539     setValue : function(v){
61540         var checked = this.checked ;
61541         this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
61542         if(this.rendered){
61543             this.el.dom.checked = this.checked;
61544             this.el.dom.defaultChecked = this.checked;
61545         }
61546         if(checked != this.checked){
61547             this.fireEvent('check', this, this.checked);
61548             if(this.handler){
61549                 this.handler.call(this.scope || this, this, this.checked);
61550             }
61551         }
61552         return this;
61553     }
61554 });
61555 Ext.reg('checkbox', Ext.form.Checkbox);
61556 /**
61557  * @class Ext.form.CheckboxGroup
61558  * @extends Ext.form.Field
61559  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
61560  * <p>Sample usage:</p>
61561  * <pre><code>
61562 var myCheckboxGroup = new Ext.form.CheckboxGroup({
61563     id:'myGroup',
61564     xtype: 'checkboxgroup',
61565     fieldLabel: 'Single Column',
61566     itemCls: 'x-check-group-alt',
61567     // Put all controls in a single column with width 100%
61568     columns: 1,
61569     items: [
61570         {boxLabel: 'Item 1', name: 'cb-col-1'},
61571         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
61572         {boxLabel: 'Item 3', name: 'cb-col-3'}
61573     ]
61574 });
61575  * </code></pre>
61576  * @constructor
61577  * Creates a new CheckboxGroup
61578  * @param {Object} config Configuration options
61579  * @xtype checkboxgroup
61580  */
61581 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
61582     /**
61583      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
61584      * to arrange in the group.
61585      */
61586     /**
61587      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
61588      * checkbox/radio controls using automatic layout.  This config can take several types of values:
61589      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
61590      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
61591      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
61592      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
61593      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
61594      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
61595      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
61596      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
61597      * container you should do so.</p></li></ul>
61598      */
61599     columns : 'auto',
61600     /**
61601      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
61602      * top to bottom before starting on the next column.  The number of controls in each column will be automatically
61603      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
61604      * to columns one at a time, completely filling each row left to right before starting on the next row.
61605      */
61606     vertical : false,
61607     /**
61608      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
61609      * If no items are selected at validation time, {@link @blankText} will be used as the error text.
61610      */
61611     allowBlank : true,
61612     /**
61613      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
61614      * select at least one item in this group")
61615      */
61616     blankText : "You must select at least one item in this group",
61617
61618     // private
61619     defaultType : 'checkbox',
61620
61621     // private
61622     groupCls : 'x-form-check-group',
61623
61624     // private
61625     initComponent: function(){
61626         this.addEvents(
61627             /**
61628              * @event change
61629              * Fires when the state of a child checkbox changes.
61630              * @param {Ext.form.CheckboxGroup} this
61631              * @param {Array} checked An array containing the checked boxes.
61632              */
61633             'change'
61634         );
61635         this.on('change', this.validate, this);
61636         Ext.form.CheckboxGroup.superclass.initComponent.call(this);
61637     },
61638
61639     // private
61640     onRender : function(ct, position){
61641         if(!this.el){
61642             var panelCfg = {
61643                 autoEl: {
61644                     id: this.id
61645                 },
61646                 cls: this.groupCls,
61647                 layout: 'column',
61648                 renderTo: ct,
61649                 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
61650             };
61651             var colCfg = {
61652                 xtype: 'container',
61653                 defaultType: this.defaultType,
61654                 layout: 'form',
61655                 defaults: {
61656                     hideLabel: true,
61657                     anchor: '100%'
61658                 }
61659             };
61660
61661             if(this.items[0].items){
61662
61663                 // The container has standard ColumnLayout configs, so pass them in directly
61664
61665                 Ext.apply(panelCfg, {
61666                     layoutConfig: {columns: this.items.length},
61667                     defaults: this.defaults,
61668                     items: this.items
61669                 });
61670                 for(var i=0, len=this.items.length; i<len; i++){
61671                     Ext.applyIf(this.items[i], colCfg);
61672                 }
61673
61674             }else{
61675
61676                 // The container has field item configs, so we have to generate the column
61677                 // panels first then move the items into the columns as needed.
61678
61679                 var numCols, cols = [];
61680
61681                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
61682                     this.columns = this.items.length;
61683                 }
61684                 if(!Ext.isArray(this.columns)){
61685                     var cs = [];
61686                     for(var i=0; i<this.columns; i++){
61687                         cs.push((100/this.columns)*.01); // distribute by even %
61688                     }
61689                     this.columns = cs;
61690                 }
61691
61692                 numCols = this.columns.length;
61693
61694                 // Generate the column configs with the correct width setting
61695                 for(var i=0; i<numCols; i++){
61696                     var cc = Ext.apply({items:[]}, colCfg);
61697                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
61698                     if(this.defaults){
61699                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults);
61700                     }
61701                     cols.push(cc);
61702                 };
61703
61704                 // Distribute the original items into the columns
61705                 if(this.vertical){
61706                     var rows = Math.ceil(this.items.length / numCols), ri = 0;
61707                     for(var i=0, len=this.items.length; i<len; i++){
61708                         if(i>0 && i%rows==0){
61709                             ri++;
61710                         }
61711                         if(this.items[i].fieldLabel){
61712                             this.items[i].hideLabel = false;
61713                         }
61714                         cols[ri].items.push(this.items[i]);
61715                     };
61716                 }else{
61717                     for(var i=0, len=this.items.length; i<len; i++){
61718                         var ci = i % numCols;
61719                         if(this.items[i].fieldLabel){
61720                             this.items[i].hideLabel = false;
61721                         }
61722                         cols[ci].items.push(this.items[i]);
61723                     };
61724                 }
61725
61726                 Ext.apply(panelCfg, {
61727                     layoutConfig: {columns: numCols},
61728                     items: cols
61729                 });
61730             }
61731
61732             this.panel = new Ext.Container(panelCfg);
61733             this.panel.ownerCt = this;
61734             this.el = this.panel.getEl();
61735
61736             if(this.forId && this.itemCls){
61737                 var l = this.el.up(this.itemCls).child('label', true);
61738                 if(l){
61739                     l.setAttribute('htmlFor', this.forId);
61740                 }
61741             }
61742
61743             var fields = this.panel.findBy(function(c){
61744                 return c.isFormField;
61745             }, this);
61746
61747             this.items = new Ext.util.MixedCollection();
61748             this.items.addAll(fields);
61749         }
61750         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
61751     },
61752
61753     initValue : function(){
61754         if(this.value){
61755             this.setValue.apply(this, this.buffered ? this.value : [this.value]);
61756             delete this.buffered;
61757             delete this.value;
61758         }
61759     },
61760
61761     afterRender : function(){
61762         Ext.form.CheckboxGroup.superclass.afterRender.call(this);
61763         this.eachItem(function(item){
61764             item.on('check', this.fireChecked, this);
61765             item.inGroup = true;
61766         });
61767     },
61768
61769     // private
61770     doLayout: function(){
61771         //ugly method required to layout hidden items
61772         if(this.rendered){
61773             this.panel.forceLayout = this.ownerCt.forceLayout;
61774             this.panel.doLayout();
61775         }
61776     },
61777
61778     // private
61779     fireChecked: function(){
61780         var arr = [];
61781         this.eachItem(function(item){
61782             if(item.checked){
61783                 arr.push(item);
61784             }
61785         });
61786         this.fireEvent('change', this, arr);
61787     },
61788     
61789     /**
61790      * Runs CheckboxGroup's validations and returns an array of any errors. The only error by default
61791      * is if allowBlank is set to true and no items are checked.
61792      * @return {Array} Array of all validation errors
61793      */
61794     getErrors: function() {
61795         var errors = Ext.form.CheckboxGroup.superclass.getErrors.apply(this, arguments);
61796         
61797         if (!this.allowBlank) {
61798             var blank = true;
61799             
61800             this.eachItem(function(f){
61801                 if (f.checked) {
61802                     return (blank = false);
61803                 }
61804             });
61805             
61806             if (blank) errors.push(this.blankText);
61807         }
61808         
61809         return errors;
61810     },
61811
61812     // private
61813     isDirty: function(){
61814         //override the behaviour to check sub items.
61815         if (this.disabled || !this.rendered) {
61816             return false;
61817         }
61818
61819         var dirty = false;
61820         
61821         this.eachItem(function(item){
61822             if(item.isDirty()){
61823                 dirty = true;
61824                 return false;
61825             }
61826         });
61827         
61828         return dirty;
61829     },
61830
61831     // private
61832     setReadOnly : function(readOnly){
61833         if(this.rendered){
61834             this.eachItem(function(item){
61835                 item.setReadOnly(readOnly);
61836             });
61837         }
61838         this.readOnly = readOnly;
61839     },
61840
61841     // private
61842     onDisable : function(){
61843         this.eachItem(function(item){
61844             item.disable();
61845         });
61846     },
61847
61848     // private
61849     onEnable : function(){
61850         this.eachItem(function(item){
61851             item.enable();
61852         });
61853     },
61854
61855     // private
61856     onResize : function(w, h){
61857         this.panel.setSize(w, h);
61858         this.panel.doLayout();
61859     },
61860
61861     // inherit docs from Field
61862     reset : function(){
61863         if (this.originalValue) {
61864             // Clear all items
61865             this.eachItem(function(c){
61866                 if(c.setValue){
61867                     c.setValue(false);
61868                     c.originalValue = c.getValue();
61869                 }
61870             });
61871             // Set items stored in originalValue, ugly - set a flag to reset the originalValue
61872             // during the horrible onSetValue.  This will allow trackResetOnLoad to function.
61873             this.resetOriginal = true;
61874             this.setValue(this.originalValue);
61875             delete this.resetOriginal;
61876         } else {
61877             this.eachItem(function(c){
61878                 if(c.reset){
61879                     c.reset();
61880                 }
61881             });
61882         }
61883         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
61884         // Important because reset is being called on both the group and the individual items.
61885         (function() {
61886             this.clearInvalid();
61887         }).defer(50, this);
61888     },
61889
61890     /**
61891      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
61892      * in the group. Examples illustrating how this method may be called:
61893      * <pre><code>
61894 // call with name and value
61895 myCheckboxGroup.setValue('cb-col-1', true);
61896 // call with an array of boolean values
61897 myCheckboxGroup.setValue([true, false, false]);
61898 // call with an object literal specifying item:value pairs
61899 myCheckboxGroup.setValue({
61900     'cb-col-2': false,
61901     'cb-col-3': true
61902 });
61903 // use comma separated string to set items with name to true (checked)
61904 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
61905      * </code></pre>
61906      * See {@link Ext.form.Checkbox#setValue} for additional information.
61907      * @param {Mixed} id The checkbox to check, or as described by example shown.
61908      * @param {Boolean} value (optional) The value to set the item.
61909      * @return {Ext.form.CheckboxGroup} this
61910      */
61911     setValue: function(){
61912         if(this.rendered){
61913             this.onSetValue.apply(this, arguments);
61914         }else{
61915             this.buffered = true;
61916             this.value = arguments;
61917         }
61918         return this;
61919     },
61920
61921     /**
61922      * @private
61923      * Sets the values of one or more of the items within the CheckboxGroup
61924      * @param {String|Array|Object} id Can take multiple forms. Can be optionally:
61925      * <ul>
61926      *   <li>An ID string to be used with a second argument</li>
61927      *   <li>An array of the form ['some', 'list', 'of', 'ids', 'to', 'mark', 'checked']</li>
61928      *   <li>An array in the form [true, true, false, true, false] etc, where each item relates to the check status of
61929      *       the checkbox at the same index</li>
61930      *   <li>An object containing ids of the checkboxes as keys and check values as properties</li>
61931      * </ul>
61932      * @param {String} value The value to set the field to if the first argument was a string
61933      */
61934     onSetValue: function(id, value){
61935         if(arguments.length == 1){
61936             if(Ext.isArray(id)){
61937                 Ext.each(id, function(val, idx){
61938                     if (Ext.isObject(val) && val.setValue){ // array of checkbox components to be checked
61939                         val.setValue(true);
61940                         if (this.resetOriginal === true) {
61941                             val.originalValue = val.getValue();
61942                         }
61943                     } else { // an array of boolean values
61944                         var item = this.items.itemAt(idx);
61945                         if(item){
61946                             item.setValue(val);
61947                         }
61948                     }
61949                 }, this);
61950             }else if(Ext.isObject(id)){
61951                 // set of name/value pairs
61952                 for(var i in id){
61953                     var f = this.getBox(i);
61954                     if(f){
61955                         f.setValue(id[i]);
61956                     }
61957                 }
61958             }else{
61959                 this.setValueForItem(id);
61960             }
61961         }else{
61962             var f = this.getBox(id);
61963             if(f){
61964                 f.setValue(value);
61965             }
61966         }
61967     },
61968
61969     // private
61970     beforeDestroy: function(){
61971         Ext.destroy(this.panel);
61972         Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
61973
61974     },
61975
61976     setValueForItem : function(val){
61977         val = String(val).split(',');
61978         this.eachItem(function(item){
61979             if(val.indexOf(item.inputValue)> -1){
61980                 item.setValue(true);
61981             }
61982         });
61983     },
61984
61985     // private
61986     getBox : function(id){
61987         var box = null;
61988         this.eachItem(function(f){
61989             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
61990                 box = f;
61991                 return false;
61992             }
61993         });
61994         return box;
61995     },
61996
61997     /**
61998      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
61999      * @return {Array} An array of the selected checkboxes.
62000      */
62001     getValue : function(){
62002         var out = [];
62003         this.eachItem(function(item){
62004             if(item.checked){
62005                 out.push(item);
62006             }
62007         });
62008         return out;
62009     },
62010
62011     /**
62012      * @private
62013      * Convenience function which passes the given function to every item in the composite
62014      * @param {Function} fn The function to call
62015      * @param {Object} scope Optional scope object
62016      */
62017     eachItem: function(fn, scope) {
62018         if(this.items && this.items.each){
62019             this.items.each(fn, scope || this);
62020         }
62021     },
62022
62023     /**
62024      * @cfg {String} name
62025      * @hide
62026      */
62027
62028     /**
62029      * @method getRawValue
62030      * @hide
62031      */
62032     getRawValue : Ext.emptyFn,
62033
62034     /**
62035      * @method setRawValue
62036      * @hide
62037      */
62038     setRawValue : Ext.emptyFn
62039
62040 });
62041
62042 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
62043 /**
62044  * @class Ext.form.CompositeField
62045  * @extends Ext.form.Field
62046  * Composite field allowing a number of form Fields to be rendered on the same row. The fields are rendered
62047  * using an hbox layout internally, so all of the normal HBox layout config items are available. Example usage:
62048  * <pre>
62049 {
62050     xtype: 'compositefield',
62051     labelWidth: 120
62052     items: [
62053         {
62054             xtype     : 'textfield',
62055             fieldLabel: 'Title',
62056             width     : 20
62057         },
62058         {
62059             xtype     : 'textfield',
62060             fieldLabel: 'First',
62061             flex      : 1
62062         },
62063         {
62064             xtype     : 'textfield',
62065             fieldLabel: 'Last',
62066             flex      : 1
62067         }
62068     ]
62069 }
62070  * </pre>
62071  * In the example above the composite's fieldLabel will be set to 'Title, First, Last' as it groups the fieldLabels
62072  * of each of its children. This can be overridden by setting a fieldLabel on the compositefield itself:
62073  * <pre>
62074 {
62075     xtype: 'compositefield',
62076     fieldLabel: 'Custom label',
62077     items: [...]
62078 }
62079  * </pre>
62080  * Any Ext.form.* component can be placed inside a composite field.
62081  */
62082 Ext.form.CompositeField = Ext.extend(Ext.form.Field, {
62083
62084     /**
62085      * @property defaultMargins
62086      * @type String
62087      * The margins to apply by default to each field in the composite
62088      */
62089     defaultMargins: '0 5 0 0',
62090
62091     /**
62092      * @property skipLastItemMargin
62093      * @type Boolean
62094      * If true, the defaultMargins are not applied to the last item in the composite field set (defaults to true)
62095      */
62096     skipLastItemMargin: true,
62097
62098     /**
62099      * @property isComposite
62100      * @type Boolean
62101      * Signifies that this is a Composite field
62102      */
62103     isComposite: true,
62104
62105     /**
62106      * @property combineErrors
62107      * @type Boolean
62108      * True to combine errors from the individual fields into a single error message at the CompositeField level (defaults to true)
62109      */
62110     combineErrors: true,
62111
62112     //inherit docs
62113     //Builds the composite field label
62114     initComponent: function() {
62115         var labels = [],
62116             items  = this.items,
62117             item;
62118
62119         for (var i=0, j = items.length; i < j; i++) {
62120             item = items[i];
62121
62122             labels.push(item.fieldLabel);
62123
62124             //apply any defaults
62125             Ext.apply(item, this.defaults);
62126
62127             //apply default margins to each item except the last
62128             if (!(i == j - 1 && this.skipLastItemMargin)) {
62129                 Ext.applyIf(item, {margins: this.defaultMargins});
62130             }
62131         }
62132
62133         this.fieldLabel = this.fieldLabel || this.buildLabel(labels);
62134
62135         /**
62136          * @property fieldErrors
62137          * @type Ext.util.MixedCollection
62138          * MixedCollection of current errors on the Composite's subfields. This is used internally to track when
62139          * to show and hide error messages at the Composite level. Listeners are attached to the MixedCollection's
62140          * add, remove and replace events to update the error icon in the UI as errors are added or removed.
62141          */
62142         this.fieldErrors = new Ext.util.MixedCollection(true, function(item) {
62143             return item.field;
62144         });
62145
62146         this.fieldErrors.on({
62147             scope  : this,
62148             add    : this.updateInvalidMark,
62149             remove : this.updateInvalidMark,
62150             replace: this.updateInvalidMark
62151         });
62152
62153         Ext.form.CompositeField.superclass.initComponent.apply(this, arguments);
62154     },
62155
62156     /**
62157      * @private
62158      * Creates an internal container using hbox and renders the fields to it
62159      */
62160     onRender: function(ct, position) {
62161         if (!this.el) {
62162             /**
62163              * @property innerCt
62164              * @type Ext.Container
62165              * A container configured with hbox layout which is responsible for laying out the subfields
62166              */
62167             var innerCt = this.innerCt = new Ext.Container({
62168                 layout  : 'hbox',
62169                 renderTo: ct,
62170                 items   : this.items,
62171                 cls     : 'x-form-composite',
62172                 defaultMargins: '0 3 0 0'
62173             });
62174
62175             this.el = innerCt.getEl();
62176
62177             var fields = innerCt.findBy(function(c) {
62178                 return c.isFormField;
62179             }, this);
62180
62181             /**
62182              * @property items
62183              * @type Ext.util.MixedCollection
62184              * Internal collection of all of the subfields in this Composite
62185              */
62186             this.items = new Ext.util.MixedCollection();
62187             this.items.addAll(fields);
62188
62189             //if we're combining subfield errors into a single message, override the markInvalid and clearInvalid
62190             //methods of each subfield and show them at the Composite level instead
62191             if (this.combineErrors) {
62192                 this.eachItem(function(field) {
62193                     Ext.apply(field, {
62194                         markInvalid : this.onFieldMarkInvalid.createDelegate(this, [field], 0),
62195                         clearInvalid: this.onFieldClearInvalid.createDelegate(this, [field], 0)
62196                     });
62197                 });
62198             }
62199
62200             //set the label 'for' to the first item
62201             var l = this.el.parent().parent().child('label', true);
62202             if (l) {
62203                 l.setAttribute('for', this.items.items[0].id);
62204             }
62205         }
62206
62207         Ext.form.CompositeField.superclass.onRender.apply(this, arguments);
62208     },
62209
62210     /**
62211      * Called if combineErrors is true and a subfield's markInvalid method is called.
62212      * By default this just adds the subfield's error to the internal fieldErrors MixedCollection
62213      * @param {Ext.form.Field} field The field that was marked invalid
62214      * @param {String} message The error message
62215      */
62216     onFieldMarkInvalid: function(field, message) {
62217         var name  = field.getName(),
62218             error = {field: name, error: message};
62219
62220         this.fieldErrors.replace(name, error);
62221
62222         field.el.addClass(field.invalidClass);
62223     },
62224
62225     /**
62226      * Called if combineErrors is true and a subfield's clearInvalid method is called.
62227      * By default this just updates the internal fieldErrors MixedCollection.
62228      * @param {Ext.form.Field} field The field that was marked invalid
62229      */
62230     onFieldClearInvalid: function(field) {
62231         this.fieldErrors.removeKey(field.getName());
62232
62233         field.el.removeClass(field.invalidClass);
62234     },
62235
62236     /**
62237      * @private
62238      * Called after a subfield is marked valid or invalid, this checks to see if any of the subfields are
62239      * currently invalid. If any subfields are invalid it builds a combined error message marks the composite
62240      * invalid, otherwise clearInvalid is called
62241      */
62242     updateInvalidMark: function() {
62243         var ieStrict = Ext.isIE6 && Ext.isStrict;
62244
62245         if (this.fieldErrors.length == 0) {
62246             this.clearInvalid();
62247
62248             //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
62249             if (ieStrict) {
62250                 this.clearInvalid.defer(50, this);
62251             }
62252         } else {
62253             var message = this.buildCombinedErrorMessage(this.fieldErrors.items);
62254
62255             this.sortErrors();
62256             this.markInvalid(message);
62257
62258             //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
62259             if (ieStrict) {
62260                 this.markInvalid(message);
62261             }
62262         }
62263     },
62264
62265     /**
62266      * Performs validation checks on each subfield and returns false if any of them fail validation.
62267      * @return {Boolean} False if any subfield failed validation
62268      */
62269     validateValue: function() {
62270         var valid = true;
62271
62272         this.eachItem(function(field) {
62273             if (!field.isValid()) valid = false;
62274         });
62275
62276         return valid;
62277     },
62278
62279     /**
62280      * Takes an object containing error messages for contained fields, returning a combined error
62281      * string (defaults to just placing each item on a new line). This can be overridden to provide
62282      * custom combined error message handling.
62283      * @param {Array} errors Array of errors in format: [{field: 'title', error: 'some error'}]
62284      * @return {String} The combined error message
62285      */
62286     buildCombinedErrorMessage: function(errors) {
62287         var combined = [],
62288             error;
62289
62290         for (var i = 0, j = errors.length; i < j; i++) {
62291             error = errors[i];
62292
62293             combined.push(String.format("{0}: {1}", error.field, error.error));
62294         }
62295
62296         return combined.join("<br />");
62297     },
62298
62299     /**
62300      * Sorts the internal fieldErrors MixedCollection by the order in which the fields are defined.
62301      * This is called before displaying errors to ensure that the errors are presented in the expected order.
62302      * This function can be overridden to provide a custom sorting order if needed.
62303      */
62304     sortErrors: function() {
62305         var fields = this.items;
62306
62307         this.fieldErrors.sort("ASC", function(a, b) {
62308             var findByName = function(key) {
62309                 return function(field) {
62310                     return field.getName() == key;
62311                 };
62312             };
62313
62314             var aIndex = fields.findIndexBy(findByName(a.field)),
62315                 bIndex = fields.findIndexBy(findByName(b.field));
62316
62317             return aIndex < bIndex ? -1 : 1;
62318         });
62319     },
62320
62321     /**
62322      * Resets each field in the composite to their previous value
62323      */
62324     reset: function() {
62325         this.eachItem(function(item) {
62326             item.reset();
62327         });
62328
62329         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
62330         // Important because reset is being called on both the group and the individual items.
62331         (function() {
62332             this.clearInvalid();
62333         }).defer(50, this);
62334     },
62335     
62336     /**
62337      * Calls clearInvalid on all child fields. This is a convenience function and should not often need to be called
62338      * as fields usually take care of clearing themselves
62339      */
62340     clearInvalidChildren: function() {
62341         this.eachItem(function(item) {
62342             item.clearInvalid();
62343         });
62344     },
62345
62346     /**
62347      * Builds a label string from an array of subfield labels.
62348      * By default this just joins the labels together with a comma
62349      * @param {Array} segments Array of each of the labels in the composite field's subfields
62350      * @return {String} The built label
62351      */
62352     buildLabel: function(segments) {
62353         return segments.join(", ");
62354     },
62355
62356     /**
62357      * Checks each field in the composite and returns true if any is dirty
62358      * @return {Boolean} True if any field is dirty
62359      */
62360     isDirty: function(){
62361         //override the behaviour to check sub items.
62362         if (this.disabled || !this.rendered) {
62363             return false;
62364         }
62365
62366         var dirty = false;
62367         this.eachItem(function(item){
62368             if(item.isDirty()){
62369                 dirty = true;
62370                 return false;
62371             }
62372         });
62373         return dirty;
62374     },
62375
62376     /**
62377      * @private
62378      * Convenience function which passes the given function to every item in the composite
62379      * @param {Function} fn The function to call
62380      * @param {Object} scope Optional scope object
62381      */
62382     eachItem: function(fn, scope) {
62383         if(this.items && this.items.each){
62384             this.items.each(fn, scope || this);
62385         }
62386     },
62387
62388     /**
62389      * @private
62390      * Passes the resize call through to the inner panel
62391      */
62392     onResize: function(adjWidth, adjHeight, rawWidth, rawHeight) {
62393         var innerCt = this.innerCt;
62394
62395         if (this.rendered && innerCt.rendered) {
62396             innerCt.setSize(adjWidth, adjHeight);
62397         }
62398
62399         Ext.form.CompositeField.superclass.onResize.apply(this, arguments);
62400     },
62401
62402     /**
62403      * @private
62404      * Forces the internal container to be laid out again
62405      */
62406     doLayout: function(shallow, force) {
62407         if (this.rendered) {
62408             var innerCt = this.innerCt;
62409
62410             innerCt.forceLayout = this.ownerCt.forceLayout;
62411             innerCt.doLayout(shallow, force);
62412         }
62413     },
62414
62415     /**
62416      * @private
62417      */
62418     beforeDestroy: function(){
62419         Ext.destroy(this.innerCt);
62420
62421         Ext.form.CompositeField.superclass.beforeDestroy.call(this);
62422     },
62423
62424     //override the behaviour to check sub items.
62425     setReadOnly : function(readOnly) {
62426         readOnly = readOnly || true;
62427
62428         if(this.rendered){
62429             this.eachItem(function(item){
62430                 item.setReadOnly(readOnly);
62431             });
62432         }
62433         this.readOnly = readOnly;
62434     },
62435
62436     onShow : function() {
62437         Ext.form.CompositeField.superclass.onShow.call(this);
62438         this.doLayout();
62439     },
62440
62441     //override the behaviour to check sub items.
62442     onDisable : function(){
62443         this.eachItem(function(item){
62444             item.disable();
62445         });
62446     },
62447
62448     //override the behaviour to check sub items.
62449     onEnable : function(){
62450         this.eachItem(function(item){
62451             item.enable();
62452         });
62453     }
62454 });
62455
62456 Ext.reg('compositefield', Ext.form.CompositeField);
62457 /**
62458  * @class Ext.form.Radio
62459  * @extends Ext.form.Checkbox
62460  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
62461  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
62462  * @constructor
62463  * Creates a new Radio
62464  * @param {Object} config Configuration options
62465  * @xtype radio
62466  */
62467 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
62468     inputType: 'radio',
62469
62470     /**
62471      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
62472      * @method
62473      */
62474     markInvalid : Ext.emptyFn,
62475     /**
62476      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
62477      * @method
62478      */
62479     clearInvalid : Ext.emptyFn,
62480
62481     /**
62482      * If this radio is part of a group, it will return the selected value
62483      * @return {String}
62484      */
62485     getGroupValue : function(){
62486         var p = this.el.up('form') || Ext.getBody();
62487         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
62488         return c ? c.value : null;
62489     },
62490
62491     // private
62492     onClick : function(){
62493         if(this.el.dom.checked != this.checked){
62494                         var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
62495                         els.each(function(el){
62496                                 if(el.dom.id == this.id){
62497                                         this.setValue(true);
62498                                 }else{
62499                                         Ext.getCmp(el.dom.id).setValue(false);
62500                                 }
62501                         }, this);
62502                 }
62503     },
62504
62505     /**
62506      * Sets either the checked/unchecked status of this Radio, or, if a string value
62507      * is passed, checks a sibling Radio of the same name whose value is the value specified.
62508      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
62509      * @return {Ext.form.Field} this
62510      */
62511     setValue : function(v){
62512         if (typeof v == 'boolean') {
62513             Ext.form.Radio.superclass.setValue.call(this, v);
62514         } else if (this.rendered) {
62515             var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
62516             if(r){
62517                 Ext.getCmp(r.id).setValue(true);
62518             }
62519         }
62520         return this;
62521     },
62522
62523     // private
62524     getCheckEl: function(){
62525         if(this.inGroup){
62526             return this.el.up('.x-form-radio-group')
62527         }
62528         return this.el.up('form') || Ext.getBody();
62529     }
62530 });
62531 Ext.reg('radio', Ext.form.Radio);
62532 /**
62533  * @class Ext.form.RadioGroup
62534  * @extends Ext.form.CheckboxGroup
62535  * A grouping container for {@link Ext.form.Radio} controls.
62536  * @constructor
62537  * Creates a new RadioGroup
62538  * @param {Object} config Configuration options
62539  * @xtype radiogroup
62540  */
62541 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
62542     /**
62543      * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
62544      * to arrange in the group.
62545      */
62546     /**
62547      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
62548      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
62549      * be used as the error text.
62550      */
62551     allowBlank : true,
62552     /**
62553      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
62554      * (defaults to 'You must select one item in this group')
62555      */
62556     blankText : 'You must select one item in this group',
62557     
62558     // private
62559     defaultType : 'radio',
62560     
62561     // private
62562     groupCls : 'x-form-radio-group',
62563     
62564     /**
62565      * @event change
62566      * Fires when the state of a child radio changes.
62567      * @param {Ext.form.RadioGroup} this
62568      * @param {Ext.form.Radio} checked The checked radio
62569      */
62570     
62571     /**
62572      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
62573      * @return {Ext.form.Radio} The selected radio.
62574      */
62575     getValue : function(){
62576         var out = null;
62577         this.eachItem(function(item){
62578             if(item.checked){
62579                 out = item;
62580                 return false;
62581             }
62582         });
62583         return out;
62584     },
62585     
62586     /**
62587      * Sets the checked radio in the group.
62588      * @param {String/Ext.form.Radio} id The radio to check.
62589      * @param {Boolean} value The value to set the radio.
62590      * @return {Ext.form.RadioGroup} this
62591      */
62592     onSetValue : function(id, value){
62593         if(arguments.length > 1){
62594             var f = this.getBox(id);
62595             if(f){
62596                 f.setValue(value);
62597                 if(f.checked){
62598                     this.eachItem(function(item){
62599                         if (item !== f){
62600                             item.setValue(false);
62601                         }
62602                     });
62603                 }
62604             }
62605         }else{
62606             this.setValueForItem(id);
62607         }
62608     },
62609     
62610     setValueForItem : function(val){
62611         val = String(val).split(',')[0];
62612         this.eachItem(function(item){
62613             item.setValue(val == item.inputValue);
62614         });
62615     },
62616     
62617     // private
62618     fireChecked : function(){
62619         if(!this.checkTask){
62620             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
62621         }
62622         this.checkTask.delay(10);
62623     },
62624     
62625     // private
62626     bufferChecked : function(){
62627         var out = null;
62628         this.eachItem(function(item){
62629             if(item.checked){
62630                 out = item;
62631                 return false;
62632             }
62633         });
62634         this.fireEvent('change', this, out);
62635     },
62636     
62637     onDestroy : function(){
62638         if(this.checkTask){
62639             this.checkTask.cancel();
62640             this.checkTask = null;
62641         }
62642         Ext.form.RadioGroup.superclass.onDestroy.call(this);
62643     }
62644
62645 });
62646
62647 Ext.reg('radiogroup', Ext.form.RadioGroup);
62648 /**
62649  * @class Ext.form.Hidden
62650  * @extends Ext.form.Field
62651  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
62652  * @constructor
62653  * Create a new Hidden field.
62654  * @param {Object} config Configuration options
62655  * @xtype hidden
62656  */
62657 Ext.form.Hidden = Ext.extend(Ext.form.Field, {
62658     // private
62659     inputType : 'hidden',
62660
62661     // private
62662     onRender : function(){
62663         Ext.form.Hidden.superclass.onRender.apply(this, arguments);
62664     },
62665
62666     // private
62667     initEvents : function(){
62668         this.originalValue = this.getValue();
62669     },
62670
62671     // These are all private overrides
62672     setSize : Ext.emptyFn,
62673     setWidth : Ext.emptyFn,
62674     setHeight : Ext.emptyFn,
62675     setPosition : Ext.emptyFn,
62676     setPagePosition : Ext.emptyFn,
62677     markInvalid : Ext.emptyFn,
62678     clearInvalid : Ext.emptyFn
62679 });
62680 Ext.reg('hidden', Ext.form.Hidden);/**
62681  * @class Ext.form.BasicForm
62682  * @extends Ext.util.Observable
62683  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
62684  * input field management, validation, submission, and form loading services.</p>
62685  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
62686  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
62687  * <p><b><u>File Uploads</u></b></p>
62688  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
62689  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
62690  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
62691  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
62692  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
62693  * but removed after the return data has been gathered.</p>
62694  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
62695  * server is using JSON to send the return object, then the
62696  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
62697  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
62698  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
62699  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
62700  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
62701  * is created containing a <tt>responseText</tt> property in order to conform to the
62702  * requirements of event handlers and callbacks.</p>
62703  * <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>
62704  * and some server technologies (notably JEE) may require some custom processing in order to
62705  * retrieve parameter names and parameter values from the packet content.</p>
62706  * @constructor
62707  * @param {Mixed} el The form element or its id
62708  * @param {Object} config Configuration options
62709  */
62710 Ext.form.BasicForm = Ext.extend(Ext.util.Observable, {
62711
62712     constructor: function(el, config){
62713         Ext.apply(this, config);
62714         if(Ext.isString(this.paramOrder)){
62715             this.paramOrder = this.paramOrder.split(/[\s,|]/);
62716         }
62717         /**
62718          * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form.
62719          * @type MixedCollection
62720          * @property items
62721          */
62722         this.items = new Ext.util.MixedCollection(false, function(o){
62723             return o.getItemId();
62724         });
62725         this.addEvents(
62726             /**
62727              * @event beforeaction
62728              * Fires before any action is performed. Return false to cancel the action.
62729              * @param {Form} this
62730              * @param {Action} action The {@link Ext.form.Action} to be performed
62731              */
62732             'beforeaction',
62733             /**
62734              * @event actionfailed
62735              * Fires when an action fails.
62736              * @param {Form} this
62737              * @param {Action} action The {@link Ext.form.Action} that failed
62738              */
62739             'actionfailed',
62740             /**
62741              * @event actioncomplete
62742              * Fires when an action is completed.
62743              * @param {Form} this
62744              * @param {Action} action The {@link Ext.form.Action} that completed
62745              */
62746             'actioncomplete'
62747         );
62748
62749         if(el){
62750             this.initEl(el);
62751         }
62752         Ext.form.BasicForm.superclass.constructor.call(this);
62753     },
62754
62755     /**
62756      * @cfg {String} method
62757      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
62758      */
62759     /**
62760      * @cfg {DataReader} reader
62761      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
62762      * data when executing 'load' actions. This is optional as there is built-in
62763      * support for processing JSON.  For additional information on using an XMLReader
62764      * see the example provided in examples/form/xml-form.html.
62765      */
62766     /**
62767      * @cfg {DataReader} errorReader
62768      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
62769      * read field error messages returned from 'submit' actions. This is optional
62770      * as there is built-in support for processing JSON.</p>
62771      * <p>The Records which provide messages for the invalid Fields must use the
62772      * Field name (or id) as the Record ID, and must contain a field called 'msg'
62773      * which contains the error message.</p>
62774      * <p>The errorReader does not have to be a full-blown implementation of a
62775      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
62776      * which returns an Array of Records in an object with the following
62777      * structure:</p><pre><code>
62778 {
62779     records: recordArray
62780 }
62781 </code></pre>
62782      */
62783     /**
62784      * @cfg {String} url
62785      * The URL to use for form actions if one isn't supplied in the
62786      * <code>{@link #doAction doAction} options</code>.
62787      */
62788     /**
62789      * @cfg {Boolean} fileUpload
62790      * Set to true if this form is a file upload.
62791      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
62792      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
62793      * DOM <tt>&lt;form></tt> element temporarily modified to have its
62794      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
62795      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
62796      * but removed after the return data has been gathered.</p>
62797      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
62798      * server is using JSON to send the return object, then the
62799      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
62800      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
62801      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
62802      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
62803      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
62804      * is created containing a <tt>responseText</tt> property in order to conform to the
62805      * requirements of event handlers and callbacks.</p>
62806      * <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>
62807      * and some server technologies (notably JEE) may require some custom processing in order to
62808      * retrieve parameter names and parameter values from the packet content.</p>
62809      */
62810     /**
62811      * @cfg {Object} baseParams
62812      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
62813      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
62814      */
62815     /**
62816      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
62817      */
62818     timeout: 30,
62819
62820     /**
62821      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
62822      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
62823      * Methods which have been imported by Ext.Direct can be specified here to load and submit
62824      * forms.
62825      * Such as the following:<pre><code>
62826 api: {
62827     load: App.ss.MyProfile.load,
62828     submit: App.ss.MyProfile.submit
62829 }
62830 </code></pre>
62831      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
62832      * to customize how the load method is invoked.
62833      * Submit actions will always use a standard form submit. The formHandler configuration must
62834      * be set on the associated server-side method which has been imported by Ext.Direct</p>
62835      */
62836
62837     /**
62838      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
62839      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
62840      * <code>load</code> configuration.</p>
62841      * <br><p>Specify the params in the order in which they must be executed on the
62842      * server-side as either (1) an Array of String values, or (2) a String of params
62843      * delimited by either whitespace, comma, or pipe. For example,
62844      * any of the following would be acceptable:</p><pre><code>
62845 paramOrder: ['param1','param2','param3']
62846 paramOrder: 'param1 param2 param3'
62847 paramOrder: 'param1,param2,param3'
62848 paramOrder: 'param1|param2|param'
62849      </code></pre>
62850      */
62851     paramOrder: undefined,
62852
62853     /**
62854      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
62855      * <code>load</code> configuration. Send parameters as a collection of named
62856      * arguments (defaults to <tt>false</tt>). Providing a
62857      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
62858      */
62859     paramsAsHash: false,
62860
62861     /**
62862      * @cfg {String} waitTitle
62863      * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
62864      */
62865     waitTitle: 'Please Wait...',
62866
62867     // private
62868     activeAction : null,
62869
62870     /**
62871      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
62872      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
62873      */
62874     trackResetOnLoad : false,
62875
62876     /**
62877      * @cfg {Boolean} standardSubmit
62878      * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
62879      * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
62880      * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
62881      * <code>options</code> to <code>{@link #submit}</code> are ignored because
62882      * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
62883      * <code>baseParams</code> and <code>params</code>), utilize hidden fields
62884      * to submit extra data, for example:</p>
62885      * <pre><code>
62886 new Ext.FormPanel({
62887     standardSubmit: true,
62888     baseParams: {
62889         foo: 'bar'
62890     },
62891     {@link url}: 'myProcess.php',
62892     items: [{
62893         xtype: 'textfield',
62894         name: 'userName'
62895     }],
62896     buttons: [{
62897         text: 'Save',
62898         handler: function(){
62899             var fp = this.ownerCt.ownerCt,
62900                 form = fp.getForm();
62901             if (form.isValid()) {
62902                 // check if there are baseParams and if
62903                 // hiddent items have been added already
62904                 if (fp.baseParams && !fp.paramsAdded) {
62905                     // add hidden items for all baseParams
62906                     for (i in fp.baseParams) {
62907                         fp.add({
62908                             xtype: 'hidden',
62909                             name: i,
62910                             value: fp.baseParams[i]
62911                         });
62912                     }
62913                     fp.doLayout();
62914                     // set a custom flag to prevent re-adding
62915                     fp.paramsAdded = true;
62916                 }
62917                 form.{@link #submit}();
62918             }
62919         }
62920     }]
62921 });
62922      * </code></pre>
62923      */
62924     /**
62925      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
62926      * element by passing it or its id or mask the form itself by passing in true.
62927      * @type Mixed
62928      * @property waitMsgTarget
62929      */
62930
62931     // private
62932     initEl : function(el){
62933         this.el = Ext.get(el);
62934         this.id = this.el.id || Ext.id();
62935         if(!this.standardSubmit){
62936             this.el.on('submit', this.onSubmit, this);
62937         }
62938         this.el.addClass('x-form');
62939     },
62940
62941     /**
62942      * Get the HTML form Element
62943      * @return Ext.Element
62944      */
62945     getEl: function(){
62946         return this.el;
62947     },
62948
62949     // private
62950     onSubmit : function(e){
62951         e.stopEvent();
62952     },
62953
62954     /**
62955      * Destroys this object.
62956      * @private
62957      * @param {Boolean} bound true if the object is bound to a form panel. If this is the case
62958      * the FormPanel will take care of destroying certain things, so we're just doubling up.
62959      */
62960     destroy: function(bound){
62961         if(bound !== true){
62962             this.items.each(function(f){
62963                 Ext.destroy(f);
62964             });
62965             Ext.destroy(this.el);
62966         }
62967         this.items.clear();
62968         this.purgeListeners();
62969     },
62970
62971     /**
62972      * Returns true if client-side validation on the form is successful.
62973      * @return Boolean
62974      */
62975     isValid : function(){
62976         var valid = true;
62977         this.items.each(function(f){
62978            if(!f.validate()){
62979                valid = false;
62980            }
62981         });
62982         return valid;
62983     },
62984
62985     /**
62986      * <p>Returns true if any fields in this form have changed from their original values.</p>
62987      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
62988      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
62989      * or {@link #loadRecord}.</p>
62990      * @return Boolean
62991      */
62992     isDirty : function(){
62993         var dirty = false;
62994         this.items.each(function(f){
62995            if(f.isDirty()){
62996                dirty = true;
62997                return false;
62998            }
62999         });
63000         return dirty;
63001     },
63002
63003     /**
63004      * Performs a predefined action ({@link Ext.form.Action.Submit} or
63005      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
63006      * to perform application-specific processing.
63007      * @param {String/Object} actionName The name of the predefined action type,
63008      * or instance of {@link Ext.form.Action} to perform.
63009      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
63010      * All of the config options listed below are supported by both the
63011      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
63012      * actions unless otherwise noted (custom actions could also accept
63013      * other config options):<ul>
63014      *
63015      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
63016      * to the form's {@link #url}.)</div></li>
63017      *
63018      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
63019      * to the form's method, or POST if not defined)</div></li>
63020      *
63021      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
63022      * (defaults to the form's baseParams, or none if not defined)</p>
63023      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
63024      *
63025      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
63026      * (defaults to the form's default headers)</div></li>
63027      *
63028      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
63029      * be invoked after a successful response (see top of
63030      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
63031      * for a description of what constitutes a successful response).
63032      * The function is passed the following parameters:<ul>
63033      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
63034      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
63035      * <div class="sub-desc">The action object contains these properties of interest:<ul>
63036      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
63037      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
63038      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
63039      * </ul></div></li></ul></div></li>
63040      *
63041      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
63042      * failed transaction attempt. The function is passed the following parameters:<ul>
63043      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
63044      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
63045      * <div class="sub-desc">The action object contains these properties of interest:<ul>
63046      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
63047      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
63048      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
63049      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
63050      * </ul></div></li></ul></div></li>
63051      *
63052      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
63053      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
63054      *
63055      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
63056      * Determines whether a Form's fields are validated in a final call to
63057      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
63058      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
63059      *
63060      * @return {BasicForm} this
63061      */
63062     doAction : function(action, options){
63063         if(Ext.isString(action)){
63064             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
63065         }
63066         if(this.fireEvent('beforeaction', this, action) !== false){
63067             this.beforeAction(action);
63068             action.run.defer(100, action);
63069         }
63070         return this;
63071     },
63072
63073     /**
63074      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
63075      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
63076      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
63077      * <p>The following code:</p><pre><code>
63078 myFormPanel.getForm().submit({
63079     clientValidation: true,
63080     url: 'updateConsignment.php',
63081     params: {
63082         newStatus: 'delivered'
63083     },
63084     success: function(form, action) {
63085        Ext.Msg.alert('Success', action.result.msg);
63086     },
63087     failure: function(form, action) {
63088         switch (action.failureType) {
63089             case Ext.form.Action.CLIENT_INVALID:
63090                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
63091                 break;
63092             case Ext.form.Action.CONNECT_FAILURE:
63093                 Ext.Msg.alert('Failure', 'Ajax communication failed');
63094                 break;
63095             case Ext.form.Action.SERVER_INVALID:
63096                Ext.Msg.alert('Failure', action.result.msg);
63097        }
63098     }
63099 });
63100 </code></pre>
63101      * would process the following server response for a successful submission:<pre><code>
63102 {
63103     "success":true, // note this is Boolean, not string
63104     "msg":"Consignment updated"
63105 }
63106 </code></pre>
63107      * and the following server response for a failed submission:<pre><code>
63108 {
63109     "success":false, // note this is Boolean, not string
63110     "msg":"You do not have permission to perform this operation"
63111 }
63112 </code></pre>
63113      * @return {BasicForm} this
63114      */
63115     submit : function(options){
63116         options = options || {};
63117         if(this.standardSubmit){
63118             var v = options.clientValidation === false || this.isValid();
63119             if(v){
63120                 var el = this.el.dom;
63121                 if(this.url && Ext.isEmpty(el.action)){
63122                     el.action = this.url;
63123                 }
63124                 el.submit();
63125             }
63126             return v;
63127         }
63128         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
63129         this.doAction(submitAction, options);
63130         return this;
63131     },
63132
63133     /**
63134      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
63135      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
63136      * @return {BasicForm} this
63137      */
63138     load : function(options){
63139         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
63140         this.doAction(loadAction, options);
63141         return this;
63142     },
63143
63144     /**
63145      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
63146      * @param {Record} record The record to edit
63147      * @return {BasicForm} this
63148      */
63149     updateRecord : function(record){
63150         record.beginEdit();
63151         var fs = record.fields;
63152         fs.each(function(f){
63153             var field = this.findField(f.name);
63154             if(field){
63155                 record.set(f.name, field.getValue());
63156             }
63157         }, this);
63158         record.endEdit();
63159         return this;
63160     },
63161
63162     /**
63163      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
63164      * {@link Ext.data.Record#data record data}.
63165      * See also {@link #trackResetOnLoad}.
63166      * @param {Record} record The record to load
63167      * @return {BasicForm} this
63168      */
63169     loadRecord : function(record){
63170         this.setValues(record.data);
63171         return this;
63172     },
63173
63174     // private
63175     beforeAction : function(action){
63176         // Call HtmlEditor's syncValue before actions
63177         this.items.each(function(f){
63178             if(f.isFormField && f.syncValue){
63179                 f.syncValue();
63180             }
63181         });
63182         var o = action.options;
63183         if(o.waitMsg){
63184             if(this.waitMsgTarget === true){
63185                 this.el.mask(o.waitMsg, 'x-mask-loading');
63186             }else if(this.waitMsgTarget){
63187                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
63188                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
63189             }else{
63190                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
63191             }
63192         }
63193     },
63194
63195     // private
63196     afterAction : function(action, success){
63197         this.activeAction = null;
63198         var o = action.options;
63199         if(o.waitMsg){
63200             if(this.waitMsgTarget === true){
63201                 this.el.unmask();
63202             }else if(this.waitMsgTarget){
63203                 this.waitMsgTarget.unmask();
63204             }else{
63205                 Ext.MessageBox.updateProgress(1);
63206                 Ext.MessageBox.hide();
63207             }
63208         }
63209         if(success){
63210             if(o.reset){
63211                 this.reset();
63212             }
63213             Ext.callback(o.success, o.scope, [this, action]);
63214             this.fireEvent('actioncomplete', this, action);
63215         }else{
63216             Ext.callback(o.failure, o.scope, [this, action]);
63217             this.fireEvent('actionfailed', this, action);
63218         }
63219     },
63220
63221     /**
63222      * Find a {@link Ext.form.Field} in this form.
63223      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
63224      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
63225      * @return Field
63226      */
63227     findField : function(id) {
63228         var field = this.items.get(id);
63229
63230         if (!Ext.isObject(field)) {
63231             //searches for the field corresponding to the given id. Used recursively for composite fields
63232             var findMatchingField = function(f) {
63233                 if (f.isFormField) {
63234                     if (f.dataIndex == id || f.id == id || f.getName() == id) {
63235                         field = f;
63236                         return false;
63237                     } else if (f.isComposite) {
63238                         return f.items.each(findMatchingField);
63239                     }
63240                 }
63241             };
63242
63243             this.items.each(findMatchingField);
63244         }
63245         return field || null;
63246     },
63247
63248
63249     /**
63250      * Mark fields in this form invalid in bulk.
63251      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
63252      * @return {BasicForm} this
63253      */
63254     markInvalid : function(errors){
63255         if (Ext.isArray(errors)) {
63256             for(var i = 0, len = errors.length; i < len; i++){
63257                 var fieldError = errors[i];
63258                 var f = this.findField(fieldError.id);
63259                 if(f){
63260                     f.markInvalid(fieldError.msg);
63261                 }
63262             }
63263         } else {
63264             var field, id;
63265             for(id in errors){
63266                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
63267                     field.markInvalid(errors[id]);
63268                 }
63269             }
63270         }
63271
63272         return this;
63273     },
63274
63275     /**
63276      * Set values for fields in this form in bulk.
63277      * @param {Array/Object} values Either an array in the form:<pre><code>
63278 [{id:'clientName', value:'Fred. Olsen Lines'},
63279  {id:'portOfLoading', value:'FXT'},
63280  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
63281      * or an object hash of the form:<pre><code>
63282 {
63283     clientName: 'Fred. Olsen Lines',
63284     portOfLoading: 'FXT',
63285     portOfDischarge: 'OSL'
63286 }</code></pre>
63287      * @return {BasicForm} this
63288      */
63289     setValues : function(values){
63290         if(Ext.isArray(values)){ // array of objects
63291             for(var i = 0, len = values.length; i < len; i++){
63292                 var v = values[i];
63293                 var f = this.findField(v.id);
63294                 if(f){
63295                     f.setValue(v.value);
63296                     if(this.trackResetOnLoad){
63297                         f.originalValue = f.getValue();
63298                     }
63299                 }
63300             }
63301         }else{ // object hash
63302             var field, id;
63303             for(id in values){
63304                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
63305                     field.setValue(values[id]);
63306                     if(this.trackResetOnLoad){
63307                         field.originalValue = field.getValue();
63308                     }
63309                 }
63310             }
63311         }
63312         return this;
63313     },
63314
63315     /**
63316      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
63317      * If multiple fields exist with the same name they are returned as an array.</p>
63318      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
63319      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
63320      * value can potentially be the emptyText of a field.</p>
63321      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
63322      * @return {String/Object}
63323      */
63324     getValues : function(asString){
63325         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
63326         if(asString === true){
63327             return fs;
63328         }
63329         return Ext.urlDecode(fs);
63330     },
63331
63332     /**
63333      * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method.
63334      * If multiple fields exist with the same name they are returned as an array.
63335      * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty.
63336      * @return {Object} The values in the form
63337      */
63338     getFieldValues : function(dirtyOnly){
63339         var o = {},
63340             n,
63341             key,
63342             val;
63343         this.items.each(function(f) {
63344             if (dirtyOnly !== true || f.isDirty()) {
63345                 n = f.getName();
63346                 key = o[n];
63347                 val = f.getValue();
63348
63349                 if(Ext.isDefined(key)){
63350                     if(Ext.isArray(key)){
63351                         o[n].push(val);
63352                     }else{
63353                         o[n] = [key, val];
63354                     }
63355                 }else{
63356                     o[n] = val;
63357                 }
63358             }
63359         });
63360         return o;
63361     },
63362
63363     /**
63364      * Clears all invalid messages in this form.
63365      * @return {BasicForm} this
63366      */
63367     clearInvalid : function(){
63368         this.items.each(function(f){
63369            f.clearInvalid();
63370         });
63371         return this;
63372     },
63373
63374     /**
63375      * Resets this form.
63376      * @return {BasicForm} this
63377      */
63378     reset : function(){
63379         this.items.each(function(f){
63380             f.reset();
63381         });
63382         return this;
63383     },
63384
63385     /**
63386      * Add Ext.form Components to this form's Collection. This does not result in rendering of
63387      * the passed Component, it just enables the form to validate Fields, and distribute values to
63388      * Fields.
63389      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
63390      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
63391      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
63392      * collection.</b></p>
63393      * @param {Field} field1
63394      * @param {Field} field2 (optional)
63395      * @param {Field} etc (optional)
63396      * @return {BasicForm} this
63397      */
63398     add : function(){
63399         this.items.addAll(Array.prototype.slice.call(arguments, 0));
63400         return this;
63401     },
63402
63403     /**
63404      * Removes a field from the items collection (does NOT remove its markup).
63405      * @param {Field} field
63406      * @return {BasicForm} this
63407      */
63408     remove : function(field){
63409         this.items.remove(field);
63410         return this;
63411     },
63412
63413     /**
63414      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
63415      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
63416      * @return {BasicForm} this
63417      */
63418     render : function(){
63419         this.items.each(function(f){
63420             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
63421                 f.applyToMarkup(f.id);
63422             }
63423         });
63424         return this;
63425     },
63426
63427     /**
63428      * Calls {@link Ext#apply} for all fields in this form with the passed object.
63429      * @param {Object} values
63430      * @return {BasicForm} this
63431      */
63432     applyToFields : function(o){
63433         this.items.each(function(f){
63434            Ext.apply(f, o);
63435         });
63436         return this;
63437     },
63438
63439     /**
63440      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
63441      * @param {Object} values
63442      * @return {BasicForm} this
63443      */
63444     applyIfToFields : function(o){
63445         this.items.each(function(f){
63446            Ext.applyIf(f, o);
63447         });
63448         return this;
63449     },
63450
63451     callFieldMethod : function(fnName, args){
63452         args = args || [];
63453         this.items.each(function(f){
63454             if(Ext.isFunction(f[fnName])){
63455                 f[fnName].apply(f, args);
63456             }
63457         });
63458         return this;
63459     }
63460 });
63461
63462 // back compat
63463 Ext.BasicForm = Ext.form.BasicForm;
63464 /**
63465  * @class Ext.form.FormPanel
63466  * @extends Ext.Panel
63467  * <p>Standard form container.</p>
63468  *
63469  * <p><b><u>Layout</u></b></p>
63470  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
63471  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
63472  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
63473  * {@link Ext.layout.FormLayout} layout manager.</p>
63474  *
63475  * <p><b><u>BasicForm</u></b></p>
63476  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
63477  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
63478  * <div class="mdetail-params"><ul>
63479  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
63480  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
63481  * </ul></div>
63482  *
63483  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
63484  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
63485  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
63486  *
63487  * <p><b><u>Form Validation</u></b></p>
63488  * <p>For information on form validation see the following:</p>
63489  * <div class="mdetail-params"><ul>
63490  * <li>{@link Ext.form.TextField}</li>
63491  * <li>{@link Ext.form.VTypes}</li>
63492  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
63493  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
63494  * </ul></div>
63495  *
63496  * <p><b><u>Form Submission</u></b></p>
63497  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
63498  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
63499  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
63500  *
63501  * @constructor
63502  * @param {Object} config Configuration options
63503  * @xtype form
63504  */
63505 Ext.FormPanel = Ext.extend(Ext.Panel, {
63506     /**
63507      * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
63508      */
63509     /**
63510      * @cfg {Boolean} hideLabels
63511      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
63512      * <tt>false</tt>.</p>
63513      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
63514      */
63515     /**
63516      * @cfg {Number} labelPad
63517      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
63518      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
63519      */
63520     /**
63521      * @cfg {String} labelSeparator
63522      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
63523      */
63524     /**
63525      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
63526      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
63527      * for its fields) (defaults to <tt>100</tt>).
63528      */
63529     /**
63530      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
63531      */
63532     /**
63533      * @cfg {Array} buttons
63534      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
63535      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
63536      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
63537      * the form's valid/invalid state.</p>
63538      */
63539
63540
63541     /**
63542      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
63543      */
63544     minButtonWidth : 75,
63545
63546     /**
63547      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
63548      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
63549      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
63550      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
63551      * for its fields).
63552      */
63553     labelAlign : 'left',
63554
63555     /**
63556      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
63557      * regularly fires the {@link #clientvalidation} event passing that state.<br>
63558      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
63559      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
63560      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
63561      */
63562     monitorValid : false,
63563
63564     /**
63565      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
63566      */
63567     monitorPoll : 200,
63568
63569     /**
63570      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered.
63571      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
63572      */
63573     layout : 'form',
63574
63575     // private
63576     initComponent : function(){
63577         this.form = this.createForm();
63578         Ext.FormPanel.superclass.initComponent.call(this);
63579
63580         this.bodyCfg = {
63581             tag: 'form',
63582             cls: this.baseCls + '-body',
63583             method : this.method || 'POST',
63584             id : this.formId || Ext.id()
63585         };
63586         if(this.fileUpload) {
63587             this.bodyCfg.enctype = 'multipart/form-data';
63588         }
63589         this.initItems();
63590
63591         this.addEvents(
63592             /**
63593              * @event clientvalidation
63594              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
63595              * @param {Ext.form.FormPanel} this
63596              * @param {Boolean} valid true if the form has passed client-side validation
63597              */
63598             'clientvalidation'
63599         );
63600
63601         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
63602     },
63603
63604     // private
63605     createForm : function(){
63606         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
63607         return new Ext.form.BasicForm(null, config);
63608     },
63609
63610     // private
63611     initFields : function(){
63612         var f = this.form;
63613         var formPanel = this;
63614         var fn = function(c){
63615             if(formPanel.isField(c)){
63616                 f.add(c);
63617             }else if(c.findBy && c != formPanel){
63618                 formPanel.applySettings(c);
63619                 //each check required for check/radio groups.
63620                 if(c.items && c.items.each){
63621                     c.items.each(fn, this);
63622                 }
63623             }
63624         };
63625         this.items.each(fn, this);
63626     },
63627
63628     // private
63629     applySettings: function(c){
63630         var ct = c.ownerCt;
63631         Ext.applyIf(c, {
63632             labelAlign: ct.labelAlign,
63633             labelWidth: ct.labelWidth,
63634             itemCls: ct.itemCls
63635         });
63636     },
63637
63638     // private
63639     getLayoutTarget : function(){
63640         return this.form.el;
63641     },
63642
63643     /**
63644      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
63645      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
63646      */
63647     getForm : function(){
63648         return this.form;
63649     },
63650
63651     // private
63652     onRender : function(ct, position){
63653         this.initFields();
63654         Ext.FormPanel.superclass.onRender.call(this, ct, position);
63655         this.form.initEl(this.body);
63656     },
63657
63658     // private
63659     beforeDestroy : function(){
63660         this.stopMonitoring();
63661         this.form.destroy(true);
63662         Ext.FormPanel.superclass.beforeDestroy.call(this);
63663     },
63664
63665     // Determine if a Component is usable as a form Field.
63666     isField : function(c) {
63667         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
63668     },
63669
63670     // private
63671     initEvents : function(){
63672         Ext.FormPanel.superclass.initEvents.call(this);
63673         // Listeners are required here to catch bubbling events from children.
63674         this.on({
63675             scope: this,
63676             add: this.onAddEvent,
63677             remove: this.onRemoveEvent
63678         });
63679         if(this.monitorValid){ // initialize after render
63680             this.startMonitoring();
63681         }
63682     },
63683
63684     // private
63685     onAdd: function(c){
63686         Ext.FormPanel.superclass.onAdd.call(this, c);
63687         this.processAdd(c);
63688     },
63689
63690     // private
63691     onAddEvent: function(ct, c){
63692         if(ct !== this){
63693             this.processAdd(c);
63694         }
63695     },
63696
63697     // private
63698     processAdd : function(c){
63699         // If a single form Field, add it
63700         if(this.isField(c)){
63701             this.form.add(c);
63702         // If a Container, add any Fields it might contain
63703         }else if(c.findBy){
63704             this.applySettings(c);
63705             this.form.add.apply(this.form, c.findBy(this.isField));
63706         }
63707     },
63708
63709     // private
63710     onRemove: function(c){
63711         Ext.FormPanel.superclass.onRemove.call(this, c);
63712         this.processRemove(c);
63713     },
63714
63715     onRemoveEvent: function(ct, c){
63716         if(ct !== this){
63717             this.processRemove(c);
63718         }
63719     },
63720
63721     // private
63722     processRemove: function(c){
63723         if(!this.destroying){
63724             // If a single form Field, remove it
63725             if(this.isField(c)){
63726                 this.form.remove(c);
63727             // If a Container, its already destroyed by the time it gets here.  Remove any references to destroyed fields.
63728             }else if (c.findBy){
63729                 Ext.each(c.findBy(this.isField), this.form.remove, this.form);
63730             }
63731         }
63732     },
63733
63734     /**
63735      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
63736      * option "monitorValid"
63737      */
63738     startMonitoring : function(){
63739         if(!this.validTask){
63740             this.validTask = new Ext.util.TaskRunner();
63741             this.validTask.start({
63742                 run : this.bindHandler,
63743                 interval : this.monitorPoll || 200,
63744                 scope: this
63745             });
63746         }
63747     },
63748
63749     /**
63750      * Stops monitoring of the valid state of this form
63751      */
63752     stopMonitoring : function(){
63753         if(this.validTask){
63754             this.validTask.stopAll();
63755             this.validTask = null;
63756         }
63757     },
63758
63759     /**
63760      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
63761      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
63762      */
63763     load : function(){
63764         this.form.load.apply(this.form, arguments);
63765     },
63766
63767     // private
63768     onDisable : function(){
63769         Ext.FormPanel.superclass.onDisable.call(this);
63770         if(this.form){
63771             this.form.items.each(function(){
63772                  this.disable();
63773             });
63774         }
63775     },
63776
63777     // private
63778     onEnable : function(){
63779         Ext.FormPanel.superclass.onEnable.call(this);
63780         if(this.form){
63781             this.form.items.each(function(){
63782                  this.enable();
63783             });
63784         }
63785     },
63786
63787     // private
63788     bindHandler : function(){
63789         var valid = true;
63790         this.form.items.each(function(f){
63791             if(!f.isValid(true)){
63792                 valid = false;
63793                 return false;
63794             }
63795         });
63796         if(this.fbar){
63797             var fitems = this.fbar.items.items;
63798             for(var i = 0, len = fitems.length; i < len; i++){
63799                 var btn = fitems[i];
63800                 if(btn.formBind === true && btn.disabled === valid){
63801                     btn.setDisabled(!valid);
63802                 }
63803             }
63804         }
63805         this.fireEvent('clientvalidation', this, valid);
63806     }
63807 });
63808 Ext.reg('form', Ext.FormPanel);
63809
63810 Ext.form.FormPanel = Ext.FormPanel;/**
63811  * @class Ext.form.FieldSet
63812  * @extends Ext.Panel
63813  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.
63814  * <pre><code>
63815 var form = new Ext.FormPanel({
63816     title: 'Simple Form with FieldSets',
63817     labelWidth: 75, // label settings here cascade unless overridden
63818     url: 'save-form.php',
63819     frame:true,
63820     bodyStyle:'padding:5px 5px 0',
63821     width: 700,
63822     renderTo: document.body,
63823     layout:'column', // arrange items in columns
63824     defaults: {      // defaults applied to items
63825         layout: 'form',
63826         border: false,
63827         bodyStyle: 'padding:4px'
63828     },
63829     items: [{
63830         // Fieldset in Column 1
63831         xtype:'fieldset',
63832         columnWidth: 0.5,
63833         title: 'Fieldset 1',
63834         collapsible: true,
63835         autoHeight:true,
63836         defaults: {
63837             anchor: '-20' // leave room for error icon
63838         },
63839         defaultType: 'textfield',
63840         items :[{
63841                 fieldLabel: 'Field 1'
63842             }, {
63843                 fieldLabel: 'Field 2'
63844             }, {
63845                 fieldLabel: 'Field 3'
63846             }
63847         ]
63848     },{
63849         // Fieldset in Column 2 - Panel inside
63850         xtype:'fieldset',
63851         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
63852         autoHeight:true,
63853         columnWidth: 0.5,
63854         checkboxToggle: true,
63855         collapsed: true, // fieldset initially collapsed
63856         layout:'anchor',
63857         items :[{
63858             xtype: 'panel',
63859             anchor: '100%',
63860             title: 'Panel inside a fieldset',
63861             frame: true,
63862             height: 100
63863         }]
63864     }]
63865 });
63866  * </code></pre>
63867  * @constructor
63868  * @param {Object} config Configuration options
63869  * @xtype fieldset
63870  */
63871 Ext.form.FieldSet = Ext.extend(Ext.Panel, {
63872     /**
63873      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just
63874      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
63875      * to <tt>false</tt>).
63876      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.
63877      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element
63878      * is:</p><pre><code>
63879      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
63880      * </code></pre>
63881      */
63882     /**
63883      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>
63884      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).
63885      */
63886     /**
63887      * @cfg {Boolean} collapsible
63888      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically
63889      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse
63890      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.
63891      */
63892     /**
63893      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
63894      */
63895     /**
63896      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see
63897      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
63898      * This property cascades to child containers.
63899      */
63900     /**
63901      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).
63902      */
63903     baseCls : 'x-fieldset',
63904     /**
63905      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).
63906      */
63907     layout : 'form',
63908     /**
63909      * @cfg {Boolean} animCollapse
63910      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the
63911      * animation (defaults to <tt>false</tt>).
63912      */
63913     animCollapse : false,
63914
63915     // private
63916     onRender : function(ct, position){
63917         if(!this.el){
63918             this.el = document.createElement('fieldset');
63919             this.el.id = this.id;
63920             if (this.title || this.header || this.checkboxToggle) {
63921                 this.el.appendChild(document.createElement('legend')).className = this.baseCls + '-header';
63922             }
63923         }
63924
63925         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
63926
63927         if(this.checkboxToggle){
63928             var o = typeof this.checkboxToggle == 'object' ?
63929                     this.checkboxToggle :
63930                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
63931             this.checkbox = this.header.insertFirst(o);
63932             this.checkbox.dom.checked = !this.collapsed;
63933             this.mon(this.checkbox, 'click', this.onCheckClick, this);
63934         }
63935     },
63936
63937     // private
63938     onCollapse : function(doAnim, animArg){
63939         if(this.checkbox){
63940             this.checkbox.dom.checked = false;
63941         }
63942         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
63943
63944     },
63945
63946     // private
63947     onExpand : function(doAnim, animArg){
63948         if(this.checkbox){
63949             this.checkbox.dom.checked = true;
63950         }
63951         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
63952     },
63953
63954     /**
63955      * This function is called by the fieldset's checkbox when it is toggled (only applies when
63956      * checkboxToggle = true).  This method should never be called externally, but can be
63957      * overridden to provide custom behavior when the checkbox is toggled if needed.
63958      */
63959     onCheckClick : function(){
63960         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
63961     }
63962
63963     /**
63964      * @cfg {String/Number} activeItem
63965      * @hide
63966      */
63967     /**
63968      * @cfg {Mixed} applyTo
63969      * @hide
63970      */
63971     /**
63972      * @cfg {Boolean} bodyBorder
63973      * @hide
63974      */
63975     /**
63976      * @cfg {Boolean} border
63977      * @hide
63978      */
63979     /**
63980      * @cfg {Boolean/Number} bufferResize
63981      * @hide
63982      */
63983     /**
63984      * @cfg {Boolean} collapseFirst
63985      * @hide
63986      */
63987     /**
63988      * @cfg {String} defaultType
63989      * @hide
63990      */
63991     /**
63992      * @cfg {String} disabledClass
63993      * @hide
63994      */
63995     /**
63996      * @cfg {String} elements
63997      * @hide
63998      */
63999     /**
64000      * @cfg {Boolean} floating
64001      * @hide
64002      */
64003     /**
64004      * @cfg {Boolean} footer
64005      * @hide
64006      */
64007     /**
64008      * @cfg {Boolean} frame
64009      * @hide
64010      */
64011     /**
64012      * @cfg {Boolean} header
64013      * @hide
64014      */
64015     /**
64016      * @cfg {Boolean} headerAsText
64017      * @hide
64018      */
64019     /**
64020      * @cfg {Boolean} hideCollapseTool
64021      * @hide
64022      */
64023     /**
64024      * @cfg {String} iconCls
64025      * @hide
64026      */
64027     /**
64028      * @cfg {Boolean/String} shadow
64029      * @hide
64030      */
64031     /**
64032      * @cfg {Number} shadowOffset
64033      * @hide
64034      */
64035     /**
64036      * @cfg {Boolean} shim
64037      * @hide
64038      */
64039     /**
64040      * @cfg {Object/Array} tbar
64041      * @hide
64042      */
64043     /**
64044      * @cfg {Array} tools
64045      * @hide
64046      */
64047     /**
64048      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
64049      * @hide
64050      */
64051     /**
64052      * @cfg {String} xtype
64053      * @hide
64054      */
64055     /**
64056      * @property header
64057      * @hide
64058      */
64059     /**
64060      * @property footer
64061      * @hide
64062      */
64063     /**
64064      * @method focus
64065      * @hide
64066      */
64067     /**
64068      * @method getBottomToolbar
64069      * @hide
64070      */
64071     /**
64072      * @method getTopToolbar
64073      * @hide
64074      */
64075     /**
64076      * @method setIconClass
64077      * @hide
64078      */
64079     /**
64080      * @event activate
64081      * @hide
64082      */
64083     /**
64084      * @event beforeclose
64085      * @hide
64086      */
64087     /**
64088      * @event bodyresize
64089      * @hide
64090      */
64091     /**
64092      * @event close
64093      * @hide
64094      */
64095     /**
64096      * @event deactivate
64097      * @hide
64098      */
64099 });
64100 Ext.reg('fieldset', Ext.form.FieldSet);/**
64101  * @class Ext.form.HtmlEditor
64102  * @extends Ext.form.Field
64103  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
64104  * automatically hidden when needed.  These are noted in the config options where appropriate.
64105  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
64106  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
64107  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
64108  * supported by this editor.</b>
64109  * <br><br>An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
64110  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
64111  * <br><br>Example usage:
64112  * <pre><code>
64113 // Simple example rendered with default options:
64114 Ext.QuickTips.init();  // enable tooltips
64115 new Ext.form.HtmlEditor({
64116     renderTo: Ext.getBody(),
64117     width: 800,
64118     height: 300
64119 });
64120
64121 // Passed via xtype into a container and with custom options:
64122 Ext.QuickTips.init();  // enable tooltips
64123 new Ext.Panel({
64124     title: 'HTML Editor',
64125     renderTo: Ext.getBody(),
64126     width: 600,
64127     height: 300,
64128     frame: true,
64129     layout: 'fit',
64130     items: {
64131         xtype: 'htmleditor',
64132         enableColors: false,
64133         enableAlignments: false
64134     }
64135 });
64136 </code></pre>
64137  * @constructor
64138  * Create a new HtmlEditor
64139  * @param {Object} config
64140  * @xtype htmleditor
64141  */
64142
64143 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
64144     /**
64145      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
64146      */
64147     enableFormat : true,
64148     /**
64149      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
64150      */
64151     enableFontSize : true,
64152     /**
64153      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
64154      */
64155     enableColors : true,
64156     /**
64157      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
64158      */
64159     enableAlignments : true,
64160     /**
64161      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
64162      */
64163     enableLists : true,
64164     /**
64165      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
64166      */
64167     enableSourceEdit : true,
64168     /**
64169      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
64170      */
64171     enableLinks : true,
64172     /**
64173      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
64174      */
64175     enableFont : true,
64176     /**
64177      * @cfg {String} createLinkText The default text for the create link prompt
64178      */
64179     createLinkText : 'Please enter the URL for the link:',
64180     /**
64181      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
64182      */
64183     defaultLinkValue : 'http:/'+'/',
64184     /**
64185      * @cfg {Array} fontFamilies An array of available font families
64186      */
64187     fontFamilies : [
64188         'Arial',
64189         'Courier New',
64190         'Tahoma',
64191         'Times New Roman',
64192         'Verdana'
64193     ],
64194     defaultFont: 'tahoma',
64195     /**
64196      * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to &#160; (Non-breaking space) in Opera and IE6, &#8203; (Zero-width space) in all other browsers).
64197      */
64198     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',
64199
64200     // private properties
64201     actionMode: 'wrap',
64202     validationEvent : false,
64203     deferHeight: true,
64204     initialized : false,
64205     activated : false,
64206     sourceEditMode : false,
64207     onFocus : Ext.emptyFn,
64208     iframePad:3,
64209     hideMode:'offsets',
64210     defaultAutoCreate : {
64211         tag: "textarea",
64212         style:"width:500px;height:300px;",
64213         autocomplete: "off"
64214     },
64215
64216     // private
64217     initComponent : function(){
64218         this.addEvents(
64219             /**
64220              * @event initialize
64221              * Fires when the editor is fully initialized (including the iframe)
64222              * @param {HtmlEditor} this
64223              */
64224             'initialize',
64225             /**
64226              * @event activate
64227              * Fires when the editor is first receives the focus. Any insertion must wait
64228              * until after this event.
64229              * @param {HtmlEditor} this
64230              */
64231             'activate',
64232              /**
64233              * @event beforesync
64234              * Fires before the textarea is updated with content from the editor iframe. Return false
64235              * to cancel the sync.
64236              * @param {HtmlEditor} this
64237              * @param {String} html
64238              */
64239             'beforesync',
64240              /**
64241              * @event beforepush
64242              * Fires before the iframe editor is updated with content from the textarea. Return false
64243              * to cancel the push.
64244              * @param {HtmlEditor} this
64245              * @param {String} html
64246              */
64247             'beforepush',
64248              /**
64249              * @event sync
64250              * Fires when the textarea is updated with content from the editor iframe.
64251              * @param {HtmlEditor} this
64252              * @param {String} html
64253              */
64254             'sync',
64255              /**
64256              * @event push
64257              * Fires when the iframe editor is updated with content from the textarea.
64258              * @param {HtmlEditor} this
64259              * @param {String} html
64260              */
64261             'push',
64262              /**
64263              * @event editmodechange
64264              * Fires when the editor switches edit modes
64265              * @param {HtmlEditor} this
64266              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
64267              */
64268             'editmodechange'
64269         );
64270     },
64271
64272     // private
64273     createFontOptions : function(){
64274         var buf = [], fs = this.fontFamilies, ff, lc;
64275         for(var i = 0, len = fs.length; i< len; i++){
64276             ff = fs[i];
64277             lc = ff.toLowerCase();
64278             buf.push(
64279                 '<option value="',lc,'" style="font-family:',ff,';"',
64280                     (this.defaultFont == lc ? ' selected="true">' : '>'),
64281                     ff,
64282                 '</option>'
64283             );
64284         }
64285         return buf.join('');
64286     },
64287
64288     /*
64289      * Protected method that will not generally be called directly. It
64290      * is called when the editor creates its toolbar. Override this method if you need to
64291      * add custom toolbar buttons.
64292      * @param {HtmlEditor} editor
64293      */
64294     createToolbar : function(editor){
64295         var items = [];
64296         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
64297
64298
64299         function btn(id, toggle, handler){
64300             return {
64301                 itemId : id,
64302                 cls : 'x-btn-icon',
64303                 iconCls: 'x-edit-'+id,
64304                 enableToggle:toggle !== false,
64305                 scope: editor,
64306                 handler:handler||editor.relayBtnCmd,
64307                 clickEvent:'mousedown',
64308                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
64309                 overflowText: editor.buttonTips[id].title || undefined,
64310                 tabIndex:-1
64311             };
64312         }
64313
64314
64315         if(this.enableFont && !Ext.isSafari2){
64316             var fontSelectItem = new Ext.Toolbar.Item({
64317                autoEl: {
64318                     tag:'select',
64319                     cls:'x-font-select',
64320                     html: this.createFontOptions()
64321                }
64322             });
64323
64324             items.push(
64325                 fontSelectItem,
64326                 '-'
64327             );
64328         }
64329
64330         if(this.enableFormat){
64331             items.push(
64332                 btn('bold'),
64333                 btn('italic'),
64334                 btn('underline')
64335             );
64336         }
64337
64338         if(this.enableFontSize){
64339             items.push(
64340                 '-',
64341                 btn('increasefontsize', false, this.adjustFont),
64342                 btn('decreasefontsize', false, this.adjustFont)
64343             );
64344         }
64345
64346         if(this.enableColors){
64347             items.push(
64348                 '-', {
64349                     itemId:'forecolor',
64350                     cls:'x-btn-icon',
64351                     iconCls: 'x-edit-forecolor',
64352                     clickEvent:'mousedown',
64353                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
64354                     tabIndex:-1,
64355                     menu : new Ext.menu.ColorMenu({
64356                         allowReselect: true,
64357                         focus: Ext.emptyFn,
64358                         value:'000000',
64359                         plain:true,
64360                         listeners: {
64361                             scope: this,
64362                             select: function(cp, color){
64363                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
64364                                 this.deferFocus();
64365                             }
64366                         },
64367                         clickEvent:'mousedown'
64368                     })
64369                 }, {
64370                     itemId:'backcolor',
64371                     cls:'x-btn-icon',
64372                     iconCls: 'x-edit-backcolor',
64373                     clickEvent:'mousedown',
64374                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
64375                     tabIndex:-1,
64376                     menu : new Ext.menu.ColorMenu({
64377                         focus: Ext.emptyFn,
64378                         value:'FFFFFF',
64379                         plain:true,
64380                         allowReselect: true,
64381                         listeners: {
64382                             scope: this,
64383                             select: function(cp, color){
64384                                 if(Ext.isGecko){
64385                                     this.execCmd('useCSS', false);
64386                                     this.execCmd('hilitecolor', color);
64387                                     this.execCmd('useCSS', true);
64388                                     this.deferFocus();
64389                                 }else{
64390                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
64391                                     this.deferFocus();
64392                                 }
64393                             }
64394                         },
64395                         clickEvent:'mousedown'
64396                     })
64397                 }
64398             );
64399         }
64400
64401         if(this.enableAlignments){
64402             items.push(
64403                 '-',
64404                 btn('justifyleft'),
64405                 btn('justifycenter'),
64406                 btn('justifyright')
64407             );
64408         }
64409
64410         if(!Ext.isSafari2){
64411             if(this.enableLinks){
64412                 items.push(
64413                     '-',
64414                     btn('createlink', false, this.createLink)
64415                 );
64416             }
64417
64418             if(this.enableLists){
64419                 items.push(
64420                     '-',
64421                     btn('insertorderedlist'),
64422                     btn('insertunorderedlist')
64423                 );
64424             }
64425             if(this.enableSourceEdit){
64426                 items.push(
64427                     '-',
64428                     btn('sourceedit', true, function(btn){
64429                         this.toggleSourceEdit(!this.sourceEditMode);
64430                     })
64431                 );
64432             }
64433         }
64434
64435         // build the toolbar
64436         var tb = new Ext.Toolbar({
64437             renderTo: this.wrap.dom.firstChild,
64438             items: items
64439         });
64440
64441         if (fontSelectItem) {
64442             this.fontSelect = fontSelectItem.el;
64443
64444             this.mon(this.fontSelect, 'change', function(){
64445                 var font = this.fontSelect.dom.value;
64446                 this.relayCmd('fontname', font);
64447                 this.deferFocus();
64448             }, this);
64449         }
64450
64451         // stop form submits
64452         this.mon(tb.el, 'click', function(e){
64453             e.preventDefault();
64454         });
64455
64456         this.tb = tb;
64457         this.tb.doLayout();
64458     },
64459
64460     onDisable: function(){
64461         this.wrap.mask();
64462         Ext.form.HtmlEditor.superclass.onDisable.call(this);
64463     },
64464
64465     onEnable: function(){
64466         this.wrap.unmask();
64467         Ext.form.HtmlEditor.superclass.onEnable.call(this);
64468     },
64469
64470     setReadOnly: function(readOnly){
64471
64472         Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
64473         if(this.initialized){
64474             this.setDesignMode(!readOnly);
64475             var bd = this.getEditorBody();
64476             if(bd){
64477                 bd.style.cursor = this.readOnly ? 'default' : 'text';
64478             }
64479             this.disableItems(readOnly);
64480         }
64481     },
64482
64483     /**
64484      * Protected method that will not generally be called directly. It
64485      * is called when the editor initializes the iframe with HTML contents. Override this method if you
64486      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
64487      *
64488      * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility
64489      */
64490     getDocMarkup : function(){
64491         var h = Ext.fly(this.iframe).getHeight() - this.iframePad * 2;
64492         return String.format('<html><head><style type="text/css">body{border: 0; margin: 0; padding: {0}px; height: {1}px; cursor: text}</style></head><body></body></html>', this.iframePad, h);
64493     },
64494
64495     // private
64496     getEditorBody : function(){
64497         var doc = this.getDoc();
64498         return doc.body || doc.documentElement;
64499     },
64500
64501     // private
64502     getDoc : function(){
64503         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
64504     },
64505
64506     // private
64507     getWin : function(){
64508         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
64509     },
64510
64511     // private
64512     onRender : function(ct, position){
64513         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
64514         this.el.dom.style.border = '0 none';
64515         this.el.dom.setAttribute('tabIndex', -1);
64516         this.el.addClass('x-hidden');
64517         if(Ext.isIE){ // fix IE 1px bogus margin
64518             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;');
64519         }
64520         this.wrap = this.el.wrap({
64521             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
64522         });
64523
64524         this.createToolbar(this);
64525
64526         this.disableItems(true);
64527
64528         this.tb.doLayout();
64529
64530         this.createIFrame();
64531
64532         if(!this.width){
64533             var sz = this.el.getSize();
64534             this.setSize(sz.width, this.height || sz.height);
64535         }
64536         this.resizeEl = this.positionEl = this.wrap;
64537     },
64538
64539     createIFrame: function(){
64540         var iframe = document.createElement('iframe');
64541         iframe.name = Ext.id();
64542         iframe.frameBorder = '0';
64543         iframe.style.overflow = 'auto';
64544
64545         this.wrap.dom.appendChild(iframe);
64546         this.iframe = iframe;
64547
64548         this.monitorTask = Ext.TaskMgr.start({
64549             run: this.checkDesignMode,
64550             scope: this,
64551             interval:100
64552         });
64553     },
64554
64555     initFrame : function(){
64556         Ext.TaskMgr.stop(this.monitorTask);
64557         var doc = this.getDoc();
64558         this.win = this.getWin();
64559
64560         doc.open();
64561         doc.write(this.getDocMarkup());
64562         doc.close();
64563
64564         var task = { // must defer to wait for browser to be ready
64565             run : function(){
64566                 var doc = this.getDoc();
64567                 if(doc.body || doc.readyState == 'complete'){
64568                     Ext.TaskMgr.stop(task);
64569                     this.setDesignMode(true);
64570                     this.initEditor.defer(10, this);
64571                 }
64572             },
64573             interval : 10,
64574             duration:10000,
64575             scope: this
64576         };
64577         Ext.TaskMgr.start(task);
64578     },
64579
64580
64581     checkDesignMode : function(){
64582         if(this.wrap && this.wrap.dom.offsetWidth){
64583             var doc = this.getDoc();
64584             if(!doc){
64585                 return;
64586             }
64587             if(!doc.editorInitialized || this.getDesignMode() != 'on'){
64588                 this.initFrame();
64589             }
64590         }
64591     },
64592
64593     /* private
64594      * set current design mode. To enable, mode can be true or 'on', off otherwise
64595      */
64596     setDesignMode : function(mode){
64597         var doc ;
64598         if(doc = this.getDoc()){
64599             if(this.readOnly){
64600                 mode = false;
64601             }
64602             doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
64603         }
64604
64605     },
64606
64607     // private
64608     getDesignMode : function(){
64609         var doc = this.getDoc();
64610         if(!doc){ return ''; }
64611         return String(doc.designMode).toLowerCase();
64612
64613     },
64614
64615     disableItems: function(disabled){
64616         if(this.fontSelect){
64617             this.fontSelect.dom.disabled = disabled;
64618         }
64619         this.tb.items.each(function(item){
64620             if(item.getItemId() != 'sourceedit'){
64621                 item.setDisabled(disabled);
64622             }
64623         });
64624     },
64625
64626     // private
64627     onResize : function(w, h){
64628         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
64629         if(this.el && this.iframe){
64630             if(Ext.isNumber(w)){
64631                 var aw = w - this.wrap.getFrameWidth('lr');
64632                 this.el.setWidth(aw);
64633                 this.tb.setWidth(aw);
64634                 this.iframe.style.width = Math.max(aw, 0) + 'px';
64635             }
64636             if(Ext.isNumber(h)){
64637                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
64638                 this.el.setHeight(ah);
64639                 this.iframe.style.height = Math.max(ah, 0) + 'px';
64640                 var bd = this.getEditorBody();
64641                 if(bd){
64642                     bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
64643                 }
64644             }
64645         }
64646     },
64647
64648     /**
64649      * Toggles the editor between standard and source edit mode.
64650      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
64651      */
64652     toggleSourceEdit : function(sourceEditMode){
64653         var iframeHeight, elHeight;
64654         if(sourceEditMode === undefined){
64655             sourceEditMode = !this.sourceEditMode;
64656         }
64657         this.sourceEditMode = sourceEditMode === true;
64658         var btn = this.tb.getComponent('sourceedit');
64659
64660         if(btn.pressed !== this.sourceEditMode){
64661             btn.toggle(this.sourceEditMode);
64662             if(!btn.xtbHidden){
64663                 return;
64664             }
64665         }
64666         if(this.sourceEditMode){
64667             // grab the height of the containing panel before we hide the iframe
64668             ls = this.getSize();
64669
64670             iframeHeight = Ext.get(this.iframe).getHeight();
64671
64672             this.disableItems(true);
64673             this.syncValue();
64674             this.iframe.className = 'x-hidden';
64675             this.el.removeClass('x-hidden');
64676             this.el.dom.removeAttribute('tabIndex');
64677             this.el.focus();
64678             this.el.dom.style.height = iframeHeight + 'px';
64679         }else{
64680             elHeight = parseInt(this.el.dom.style.height, 10);
64681             if(this.initialized){
64682                 this.disableItems(this.readOnly);
64683             }
64684             this.pushValue();
64685             this.iframe.className = '';
64686             this.el.addClass('x-hidden');
64687             this.el.dom.setAttribute('tabIndex', -1);
64688             this.deferFocus();
64689
64690             this.setSize(ls);
64691             this.iframe.style.height = elHeight + 'px';
64692         }
64693         this.fireEvent('editmodechange', this, this.sourceEditMode);
64694     },
64695
64696     // private used internally
64697     createLink : function(){
64698         var url = prompt(this.createLinkText, this.defaultLinkValue);
64699         if(url && url != 'http:/'+'/'){
64700             this.relayCmd('createlink', url);
64701         }
64702     },
64703
64704     // private
64705     initEvents : function(){
64706         this.originalValue = this.getValue();
64707     },
64708
64709     /**
64710      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
64711      * @method
64712      */
64713     markInvalid : Ext.emptyFn,
64714
64715     /**
64716      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
64717      * @method
64718      */
64719     clearInvalid : Ext.emptyFn,
64720
64721     // docs inherit from Field
64722     setValue : function(v){
64723         Ext.form.HtmlEditor.superclass.setValue.call(this, v);
64724         this.pushValue();
64725         return this;
64726     },
64727
64728     /**
64729      * Protected method that will not generally be called directly. If you need/want
64730      * custom HTML cleanup, this is the method you should override.
64731      * @param {String} html The HTML to be cleaned
64732      * @return {String} The cleaned HTML
64733      */
64734     cleanHtml: function(html) {
64735         html = String(html);
64736         if(Ext.isWebKit){ // strip safari nonsense
64737             html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
64738         }
64739
64740         /*
64741          * Neat little hack. Strips out all the non-digit characters from the default
64742          * value and compares it to the character code of the first character in the string
64743          * because it can cause encoding issues when posted to the server.
64744          */
64745         if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
64746             html = html.substring(1);
64747         }
64748         return html;
64749     },
64750
64751     /**
64752      * Protected method that will not generally be called directly. Syncs the contents
64753      * of the editor iframe with the textarea.
64754      */
64755     syncValue : function(){
64756         if(this.initialized){
64757             var bd = this.getEditorBody();
64758             var html = bd.innerHTML;
64759             if(Ext.isWebKit){
64760                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
64761                 var m = bs.match(/text-align:(.*?);/i);
64762                 if(m && m[1]){
64763                     html = '<div style="'+m[0]+'">' + html + '</div>';
64764                 }
64765             }
64766             html = this.cleanHtml(html);
64767             if(this.fireEvent('beforesync', this, html) !== false){
64768                 this.el.dom.value = html;
64769                 this.fireEvent('sync', this, html);
64770             }
64771         }
64772     },
64773
64774     //docs inherit from Field
64775     getValue : function() {
64776         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
64777         return Ext.form.HtmlEditor.superclass.getValue.call(this);
64778     },
64779
64780     /**
64781      * Protected method that will not generally be called directly. Pushes the value of the textarea
64782      * into the iframe editor.
64783      */
64784     pushValue : function(){
64785         if(this.initialized){
64786             var v = this.el.dom.value;
64787             if(!this.activated && v.length < 1){
64788                 v = this.defaultValue;
64789             }
64790             if(this.fireEvent('beforepush', this, v) !== false){
64791                 this.getEditorBody().innerHTML = v;
64792                 if(Ext.isGecko){
64793                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
64794                     this.setDesignMode(false);  //toggle off first
64795
64796                 }
64797                 this.setDesignMode(true);
64798                 this.fireEvent('push', this, v);
64799             }
64800
64801         }
64802     },
64803
64804     // private
64805     deferFocus : function(){
64806         this.focus.defer(10, this);
64807     },
64808
64809     // docs inherit from Field
64810     focus : function(){
64811         if(this.win && !this.sourceEditMode){
64812             this.win.focus();
64813         }else{
64814             this.el.focus();
64815         }
64816     },
64817
64818     // private
64819     initEditor : function(){
64820         //Destroying the component during/before initEditor can cause issues.
64821         try{
64822             var dbody = this.getEditorBody(),
64823                 ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
64824                 doc,
64825                 fn;
64826
64827             ss['background-attachment'] = 'fixed'; // w3c
64828             dbody.bgProperties = 'fixed'; // ie
64829
64830             Ext.DomHelper.applyStyles(dbody, ss);
64831
64832             doc = this.getDoc();
64833
64834             if(doc){
64835                 try{
64836                     Ext.EventManager.removeAll(doc);
64837                 }catch(e){}
64838             }
64839
64840             /*
64841              * We need to use createDelegate here, because when using buffer, the delayed task is added
64842              * as a property to the function. When the listener is removed, the task is deleted from the function.
64843              * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
64844              * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
64845              */
64846             fn = this.onEditorEvent.createDelegate(this);
64847             Ext.EventManager.on(doc, {
64848                 mousedown: fn,
64849                 dblclick: fn,
64850                 click: fn,
64851                 keyup: fn,
64852                 buffer:100
64853             });
64854
64855             if(Ext.isGecko){
64856                 Ext.EventManager.on(doc, 'keypress', this.applyCommand, this);
64857             }
64858             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
64859                 Ext.EventManager.on(doc, 'keydown', this.fixKeys, this);
64860             }
64861             doc.editorInitialized = true;
64862             this.initialized = true;
64863             this.pushValue();
64864             this.setReadOnly(this.readOnly);
64865             this.fireEvent('initialize', this);
64866         }catch(e){}
64867     },
64868
64869     // private
64870     onDestroy : function(){
64871         if(this.monitorTask){
64872             Ext.TaskMgr.stop(this.monitorTask);
64873         }
64874         if(this.rendered){
64875             Ext.destroy(this.tb);
64876             var doc = this.getDoc();
64877             if(doc){
64878                 try{
64879                     Ext.EventManager.removeAll(doc);
64880                     for (var prop in doc){
64881                         delete doc[prop];
64882                     }
64883                 }catch(e){}
64884             }
64885             if(this.wrap){
64886                 this.wrap.dom.innerHTML = '';
64887                 this.wrap.remove();
64888             }
64889         }
64890
64891         if(this.el){
64892             this.el.removeAllListeners();
64893             this.el.remove();
64894         }
64895         this.purgeListeners();
64896     },
64897
64898     // private
64899     onFirstFocus : function(){
64900         this.activated = true;
64901         this.disableItems(this.readOnly);
64902         if(Ext.isGecko){ // prevent silly gecko errors
64903             this.win.focus();
64904             var s = this.win.getSelection();
64905             if(!s.focusNode || s.focusNode.nodeType != 3){
64906                 var r = s.getRangeAt(0);
64907                 r.selectNodeContents(this.getEditorBody());
64908                 r.collapse(true);
64909                 this.deferFocus();
64910             }
64911             try{
64912                 this.execCmd('useCSS', true);
64913                 this.execCmd('styleWithCSS', false);
64914             }catch(e){}
64915         }
64916         this.fireEvent('activate', this);
64917     },
64918
64919     // private
64920     adjustFont: function(btn){
64921         var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1,
64922             doc = this.getDoc(),
64923             v = parseInt(doc.queryCommandValue('FontSize') || 2, 10);
64924         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
64925             // Safari 3 values
64926             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
64927             if(v <= 10){
64928                 v = 1 + adjust;
64929             }else if(v <= 13){
64930                 v = 2 + adjust;
64931             }else if(v <= 16){
64932                 v = 3 + adjust;
64933             }else if(v <= 18){
64934                 v = 4 + adjust;
64935             }else if(v <= 24){
64936                 v = 5 + adjust;
64937             }else {
64938                 v = 6 + adjust;
64939             }
64940             v = v.constrain(1, 6);
64941         }else{
64942             if(Ext.isSafari){ // safari
64943                 adjust *= 2;
64944             }
64945             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
64946         }
64947         this.execCmd('FontSize', v);
64948     },
64949
64950     // private
64951     onEditorEvent : function(e){
64952         this.updateToolbar();
64953     },
64954
64955
64956     /**
64957      * Protected method that will not generally be called directly. It triggers
64958      * a toolbar update by reading the markup state of the current selection in the editor.
64959      */
64960     updateToolbar: function(){
64961
64962         if(this.readOnly){
64963             return;
64964         }
64965
64966         if(!this.activated){
64967             this.onFirstFocus();
64968             return;
64969         }
64970
64971         var btns = this.tb.items.map,
64972             doc = this.getDoc();
64973
64974         if(this.enableFont && !Ext.isSafari2){
64975             var name = (doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
64976             if(name != this.fontSelect.dom.value){
64977                 this.fontSelect.dom.value = name;
64978             }
64979         }
64980         if(this.enableFormat){
64981             btns.bold.toggle(doc.queryCommandState('bold'));
64982             btns.italic.toggle(doc.queryCommandState('italic'));
64983             btns.underline.toggle(doc.queryCommandState('underline'));
64984         }
64985         if(this.enableAlignments){
64986             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
64987             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
64988             btns.justifyright.toggle(doc.queryCommandState('justifyright'));
64989         }
64990         if(!Ext.isSafari2 && this.enableLists){
64991             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
64992             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
64993         }
64994
64995         Ext.menu.MenuMgr.hideAll();
64996
64997         this.syncValue();
64998     },
64999
65000     // private
65001     relayBtnCmd : function(btn){
65002         this.relayCmd(btn.getItemId());
65003     },
65004
65005     /**
65006      * Executes a Midas editor command on the editor document and performs necessary focus and
65007      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
65008      * @param {String} cmd The Midas command
65009      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
65010      */
65011     relayCmd : function(cmd, value){
65012         (function(){
65013             this.focus();
65014             this.execCmd(cmd, value);
65015             this.updateToolbar();
65016         }).defer(10, this);
65017     },
65018
65019     /**
65020      * Executes a Midas editor command directly on the editor document.
65021      * For visual commands, you should use {@link #relayCmd} instead.
65022      * <b>This should only be called after the editor is initialized.</b>
65023      * @param {String} cmd The Midas command
65024      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
65025      */
65026     execCmd : function(cmd, value){
65027         var doc = this.getDoc();
65028         doc.execCommand(cmd, false, value === undefined ? null : value);
65029         this.syncValue();
65030     },
65031
65032     // private
65033     applyCommand : function(e){
65034         if(e.ctrlKey){
65035             var c = e.getCharCode(), cmd;
65036             if(c > 0){
65037                 c = String.fromCharCode(c);
65038                 switch(c){
65039                     case 'b':
65040                         cmd = 'bold';
65041                     break;
65042                     case 'i':
65043                         cmd = 'italic';
65044                     break;
65045                     case 'u':
65046                         cmd = 'underline';
65047                     break;
65048                 }
65049                 if(cmd){
65050                     this.win.focus();
65051                     this.execCmd(cmd);
65052                     this.deferFocus();
65053                     e.preventDefault();
65054                 }
65055             }
65056         }
65057     },
65058
65059     /**
65060      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
65061      * to insert text.
65062      * @param {String} text
65063      */
65064     insertAtCursor : function(text){
65065         if(!this.activated){
65066             return;
65067         }
65068         if(Ext.isIE){
65069             this.win.focus();
65070             var doc = this.getDoc(),
65071                 r = doc.selection.createRange();
65072             if(r){
65073                 r.pasteHTML(text);
65074                 this.syncValue();
65075                 this.deferFocus();
65076             }
65077         }else{
65078             this.win.focus();
65079             this.execCmd('InsertHTML', text);
65080             this.deferFocus();
65081         }
65082     },
65083
65084     // private
65085     fixKeys : function(){ // load time branching for fastest keydown performance
65086         if(Ext.isIE){
65087             return function(e){
65088                 var k = e.getKey(),
65089                     doc = this.getDoc(),
65090                         r;
65091                 if(k == e.TAB){
65092                     e.stopEvent();
65093                     r = doc.selection.createRange();
65094                     if(r){
65095                         r.collapse(true);
65096                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
65097                         this.deferFocus();
65098                     }
65099                 }else if(k == e.ENTER){
65100                     r = doc.selection.createRange();
65101                     if(r){
65102                         var target = r.parentElement();
65103                         if(!target || target.tagName.toLowerCase() != 'li'){
65104                             e.stopEvent();
65105                             r.pasteHTML('<br />');
65106                             r.collapse(false);
65107                             r.select();
65108                         }
65109                     }
65110                 }
65111             };
65112         }else if(Ext.isOpera){
65113             return function(e){
65114                 var k = e.getKey();
65115                 if(k == e.TAB){
65116                     e.stopEvent();
65117                     this.win.focus();
65118                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
65119                     this.deferFocus();
65120                 }
65121             };
65122         }else if(Ext.isWebKit){
65123             return function(e){
65124                 var k = e.getKey();
65125                 if(k == e.TAB){
65126                     e.stopEvent();
65127                     this.execCmd('InsertText','\t');
65128                     this.deferFocus();
65129                 }else if(k == e.ENTER){
65130                     e.stopEvent();
65131                     this.execCmd('InsertHtml','<br /><br />');
65132                     this.deferFocus();
65133                 }
65134              };
65135         }
65136     }(),
65137
65138     /**
65139      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
65140      * @return {Ext.Toolbar}
65141      */
65142     getToolbar : function(){
65143         return this.tb;
65144     },
65145
65146     /**
65147      * Object collection of toolbar tooltips for the buttons in the editor. The key
65148      * is the command id associated with that button and the value is a valid QuickTips object.
65149      * For example:
65150 <pre><code>
65151 {
65152     bold : {
65153         title: 'Bold (Ctrl+B)',
65154         text: 'Make the selected text bold.',
65155         cls: 'x-html-editor-tip'
65156     },
65157     italic : {
65158         title: 'Italic (Ctrl+I)',
65159         text: 'Make the selected text italic.',
65160         cls: 'x-html-editor-tip'
65161     },
65162     ...
65163 </code></pre>
65164     * @type Object
65165      */
65166     buttonTips : {
65167         bold : {
65168             title: 'Bold (Ctrl+B)',
65169             text: 'Make the selected text bold.',
65170             cls: 'x-html-editor-tip'
65171         },
65172         italic : {
65173             title: 'Italic (Ctrl+I)',
65174             text: 'Make the selected text italic.',
65175             cls: 'x-html-editor-tip'
65176         },
65177         underline : {
65178             title: 'Underline (Ctrl+U)',
65179             text: 'Underline the selected text.',
65180             cls: 'x-html-editor-tip'
65181         },
65182         increasefontsize : {
65183             title: 'Grow Text',
65184             text: 'Increase the font size.',
65185             cls: 'x-html-editor-tip'
65186         },
65187         decreasefontsize : {
65188             title: 'Shrink Text',
65189             text: 'Decrease the font size.',
65190             cls: 'x-html-editor-tip'
65191         },
65192         backcolor : {
65193             title: 'Text Highlight Color',
65194             text: 'Change the background color of the selected text.',
65195             cls: 'x-html-editor-tip'
65196         },
65197         forecolor : {
65198             title: 'Font Color',
65199             text: 'Change the color of the selected text.',
65200             cls: 'x-html-editor-tip'
65201         },
65202         justifyleft : {
65203             title: 'Align Text Left',
65204             text: 'Align text to the left.',
65205             cls: 'x-html-editor-tip'
65206         },
65207         justifycenter : {
65208             title: 'Center Text',
65209             text: 'Center text in the editor.',
65210             cls: 'x-html-editor-tip'
65211         },
65212         justifyright : {
65213             title: 'Align Text Right',
65214             text: 'Align text to the right.',
65215             cls: 'x-html-editor-tip'
65216         },
65217         insertunorderedlist : {
65218             title: 'Bullet List',
65219             text: 'Start a bulleted list.',
65220             cls: 'x-html-editor-tip'
65221         },
65222         insertorderedlist : {
65223             title: 'Numbered List',
65224             text: 'Start a numbered list.',
65225             cls: 'x-html-editor-tip'
65226         },
65227         createlink : {
65228             title: 'Hyperlink',
65229             text: 'Make the selected text a hyperlink.',
65230             cls: 'x-html-editor-tip'
65231         },
65232         sourceedit : {
65233             title: 'Source Edit',
65234             text: 'Switch to source editing mode.',
65235             cls: 'x-html-editor-tip'
65236         }
65237     }
65238
65239     // hide stuff that is not compatible
65240     /**
65241      * @event blur
65242      * @hide
65243      */
65244     /**
65245      * @event change
65246      * @hide
65247      */
65248     /**
65249      * @event focus
65250      * @hide
65251      */
65252     /**
65253      * @event specialkey
65254      * @hide
65255      */
65256     /**
65257      * @cfg {String} fieldClass @hide
65258      */
65259     /**
65260      * @cfg {String} focusClass @hide
65261      */
65262     /**
65263      * @cfg {String} autoCreate @hide
65264      */
65265     /**
65266      * @cfg {String} inputType @hide
65267      */
65268     /**
65269      * @cfg {String} invalidClass @hide
65270      */
65271     /**
65272      * @cfg {String} invalidText @hide
65273      */
65274     /**
65275      * @cfg {String} msgFx @hide
65276      */
65277     /**
65278      * @cfg {String} validateOnBlur @hide
65279      */
65280     /**
65281      * @cfg {Boolean} allowDomMove  @hide
65282      */
65283     /**
65284      * @cfg {String} applyTo @hide
65285      */
65286     /**
65287      * @cfg {String} autoHeight  @hide
65288      */
65289     /**
65290      * @cfg {String} autoWidth  @hide
65291      */
65292     /**
65293      * @cfg {String} cls  @hide
65294      */
65295     /**
65296      * @cfg {String} disabled  @hide
65297      */
65298     /**
65299      * @cfg {String} disabledClass  @hide
65300      */
65301     /**
65302      * @cfg {String} msgTarget  @hide
65303      */
65304     /**
65305      * @cfg {String} readOnly  @hide
65306      */
65307     /**
65308      * @cfg {String} style  @hide
65309      */
65310     /**
65311      * @cfg {String} validationDelay  @hide
65312      */
65313     /**
65314      * @cfg {String} validationEvent  @hide
65315      */
65316     /**
65317      * @cfg {String} tabIndex  @hide
65318      */
65319     /**
65320      * @property disabled
65321      * @hide
65322      */
65323     /**
65324      * @method applyToMarkup
65325      * @hide
65326      */
65327     /**
65328      * @method disable
65329      * @hide
65330      */
65331     /**
65332      * @method enable
65333      * @hide
65334      */
65335     /**
65336      * @method validate
65337      * @hide
65338      */
65339     /**
65340      * @event valid
65341      * @hide
65342      */
65343     /**
65344      * @method setDisabled
65345      * @hide
65346      */
65347     /**
65348      * @cfg keys
65349      * @hide
65350      */
65351 });
65352 Ext.reg('htmleditor', Ext.form.HtmlEditor);/**
65353  * @class Ext.form.TimeField
65354  * @extends Ext.form.ComboBox
65355  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:
65356  * <pre><code>
65357 new Ext.form.TimeField({
65358     minValue: '9:00 AM',
65359     maxValue: '6:00 PM',
65360     increment: 30
65361 });
65362 </code></pre>
65363  * @constructor
65364  * Create a new TimeField
65365  * @param {Object} config
65366  * @xtype timefield
65367  */
65368 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
65369     /**
65370      * @cfg {Date/String} minValue
65371      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
65372      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
65373      */
65374     minValue : undefined,
65375     /**
65376      * @cfg {Date/String} maxValue
65377      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
65378      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
65379      */
65380     maxValue : undefined,
65381     /**
65382      * @cfg {String} minText
65383      * The error text to display when the date in the cell is before minValue (defaults to
65384      * 'The time in this field must be equal to or after {0}').
65385      */
65386     minText : "The time in this field must be equal to or after {0}",
65387     /**
65388      * @cfg {String} maxText
65389      * The error text to display when the time is after maxValue (defaults to
65390      * 'The time in this field must be equal to or before {0}').
65391      */
65392     maxText : "The time in this field must be equal to or before {0}",
65393     /**
65394      * @cfg {String} invalidText
65395      * The error text to display when the time in the field is invalid (defaults to
65396      * '{value} is not a valid time').
65397      */
65398     invalidText : "{0} is not a valid time",
65399     /**
65400      * @cfg {String} format
65401      * The default time format string which can be overriden for localization support.  The format must be
65402      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time
65403      * format try 'H:i' instead.
65404      */
65405     format : "g:i A",
65406     /**
65407      * @cfg {String} altFormats
65408      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
65409      * format (defaults to 'g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A').
65410      */
65411     altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",
65412     /**
65413      * @cfg {Number} increment
65414      * The number of minutes between each time value in the list (defaults to 15).
65415      */
65416     increment: 15,
65417
65418     // private override
65419     mode: 'local',
65420     // private override
65421     triggerAction: 'all',
65422     // private override
65423     typeAhead: false,
65424
65425     // private - This is the date to use when generating time values in the absence of either minValue
65426     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an
65427     // arbitrary "safe" date that can be any date aside from DST boundary dates.
65428     initDate: '1/1/2008',
65429
65430     initDateFormat: 'j/n/Y',
65431
65432     // private
65433     initComponent : function(){
65434         if(Ext.isDefined(this.minValue)){
65435             this.setMinValue(this.minValue, true);
65436         }
65437         if(Ext.isDefined(this.maxValue)){
65438             this.setMaxValue(this.maxValue, true);
65439         }
65440         if(!this.store){
65441             this.generateStore(true);
65442         }
65443         Ext.form.TimeField.superclass.initComponent.call(this);
65444     },
65445
65446     /**
65447      * Replaces any existing {@link #minValue} with the new time and refreshes the store.
65448      * @param {Date/String} value The minimum time that can be selected
65449      */
65450     setMinValue: function(value, /* private */ initial){
65451         this.setLimit(value, true, initial);
65452         return this;
65453     },
65454
65455     /**
65456      * Replaces any existing {@link #maxValue} with the new time and refreshes the store.
65457      * @param {Date/String} value The maximum time that can be selected
65458      */
65459     setMaxValue: function(value, /* private */ initial){
65460         this.setLimit(value, false, initial);
65461         return this;
65462     },
65463
65464     // private
65465     generateStore: function(initial){
65466         var min = this.minValue || new Date(this.initDate).clearTime(),
65467             max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),
65468             times = [];
65469
65470         while(min <= max){
65471             times.push(min.dateFormat(this.format));
65472             min = min.add('mi', this.increment);
65473         }
65474         this.bindStore(times, initial);
65475     },
65476
65477     // private
65478     setLimit: function(value, isMin, initial){
65479         var d;
65480         if(Ext.isString(value)){
65481             d = this.parseDate(value);
65482         }else if(Ext.isDate(value)){
65483             d = value;
65484         }
65485         if(d){
65486             var val = new Date(this.initDate).clearTime();
65487             val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
65488             this[isMin ? 'minValue' : 'maxValue'] = val;
65489             if(!initial){
65490                 this.generateStore();
65491             }
65492         }
65493     },
65494
65495     // inherited docs
65496     getValue : function(){
65497         var v = Ext.form.TimeField.superclass.getValue.call(this);
65498         return this.formatDate(this.parseDate(v)) || '';
65499     },
65500
65501     // inherited docs
65502     setValue : function(value){
65503         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));
65504     },
65505
65506     // private overrides
65507     validateValue : Ext.form.DateField.prototype.validateValue,
65508
65509     formatDate : Ext.form.DateField.prototype.formatDate,
65510
65511     parseDate: function(value) {
65512         if (!value || Ext.isDate(value)) {
65513             return value;
65514         }
65515
65516         var id = this.initDate + ' ',
65517             idf = this.initDateFormat + ' ',
65518             v = Date.parseDate(id + value, idf + this.format), // *** handle DST. note: this.format is a TIME-only format
65519             af = this.altFormats;
65520
65521         if (!v && af) {
65522             if (!this.altFormatsArray) {
65523                 this.altFormatsArray = af.split("|");
65524             }
65525             for (var i = 0, afa = this.altFormatsArray, len = afa.length; i < len && !v; i++) {
65526                 v = Date.parseDate(id + value, idf + afa[i]);
65527             }
65528         }
65529
65530         return v;
65531     }
65532 });
65533 Ext.reg('timefield', Ext.form.TimeField);/**
65534  * @class Ext.form.SliderField
65535  * @extends Ext.form.Field
65536  * Wraps a {@link Ext.Slider Slider} so it can be used as a form field.
65537  * @constructor
65538  * Creates a new SliderField
65539  * @param {Object} config Configuration options. Note that you can pass in any slider configuration options, as well as
65540  * as any field configuration options.
65541  * @xtype sliderfield
65542  */
65543 Ext.form.SliderField = Ext.extend(Ext.form.Field, {
65544     
65545     /**
65546      * @cfg {Boolean} useTips
65547      * True to use an Ext.slider.Tip to display tips for the value. Defaults to <tt>true</tt>.
65548      */
65549     useTips : true,
65550     
65551     /**
65552      * @cfg {Function} tipText
65553      * A function used to display custom text for the slider tip. Defaults to <tt>null</tt>, which will
65554      * use the default on the plugin.
65555      */
65556     tipText : null,
65557     
65558     // private override
65559     actionMode: 'wrap',
65560     
65561     /**
65562      * Initialize the component.
65563      * @private
65564      */
65565     initComponent : function() {
65566         var cfg = Ext.copyTo({
65567             id: this.id + '-slider'
65568         }, this.initialConfig, ['vertical', 'minValue', 'maxValue', 'decimalPrecision', 'keyIncrement', 'increment', 'clickToChange', 'animate']);
65569         
65570         // only can use it if it exists.
65571         if (this.useTips) {
65572             var plug = this.tipText ? {getText: this.tipText} : {};
65573             cfg.plugins = [new Ext.slider.Tip(plug)];
65574         }
65575         this.slider = new Ext.Slider(cfg);
65576         Ext.form.SliderField.superclass.initComponent.call(this);
65577     },    
65578     
65579     /**
65580      * Set up the hidden field
65581      * @param {Object} ct The container to render to.
65582      * @param {Object} position The position in the container to render to.
65583      * @private
65584      */
65585     onRender : function(ct, position){
65586         this.autoCreate = {
65587             id: this.id,
65588             name: this.name,
65589             type: 'hidden',
65590             tag: 'input'    
65591         };
65592         Ext.form.SliderField.superclass.onRender.call(this, ct, position);
65593         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
65594         this.slider.render(this.wrap);
65595     },
65596     
65597     /**
65598      * Ensure that the slider size is set automatically when the field resizes.
65599      * @param {Object} w The width
65600      * @param {Object} h The height
65601      * @param {Object} aw The adjusted width
65602      * @param {Object} ah The adjusted height
65603      * @private
65604      */
65605     onResize : function(w, h, aw, ah){
65606         Ext.form.SliderField.superclass.onResize.call(this, w, h, aw, ah);
65607         this.slider.setSize(w, h);    
65608     },
65609     
65610     /**
65611      * Initialize any events for this class.
65612      * @private
65613      */
65614     initEvents : function(){
65615         Ext.form.SliderField.superclass.initEvents.call(this);
65616         this.slider.on('change', this.onChange, this);   
65617     },
65618     
65619     /**
65620      * Utility method to set the value of the field when the slider changes.
65621      * @param {Object} slider The slider object.
65622      * @param {Object} v The new value.
65623      * @private
65624      */
65625     onChange : function(slider, v){
65626         this.setValue(v, undefined, true);
65627     },
65628     
65629     /**
65630      * Enable the slider when the field is enabled.
65631      * @private
65632      */
65633     onEnable : function(){
65634         Ext.form.SliderField.superclass.onEnable.call(this);
65635         this.slider.enable();
65636     },
65637     
65638     /**
65639      * Disable the slider when the field is disabled.
65640      * @private
65641      */
65642     onDisable : function(){
65643         Ext.form.SliderField.superclass.onDisable.call(this);
65644         this.slider.disable();    
65645     },
65646     
65647     /**
65648      * Ensure the slider is destroyed when the field is destroyed.
65649      * @private
65650      */
65651     beforeDestroy : function(){
65652         Ext.destroy(this.slider);
65653         Ext.form.SliderField.superclass.beforeDestroy.call(this);
65654     },
65655     
65656     /**
65657      * If a side icon is shown, do alignment to the slider
65658      * @private
65659      */
65660     alignErrorIcon : function(){
65661         this.errorIcon.alignTo(this.slider.el, 'tl-tr', [2, 0]);
65662     },
65663     
65664     /**
65665      * Sets the minimum field value.
65666      * @param {Number} v The new minimum value.
65667      * @return {Ext.form.SliderField} this
65668      */
65669     setMinValue : function(v){
65670         this.slider.setMinValue(v);
65671         return this;    
65672     },
65673     
65674     /**
65675      * Sets the maximum field value.
65676      * @param {Number} v The new maximum value.
65677      * @return {Ext.form.SliderField} this
65678      */
65679     setMaxValue : function(v){
65680         this.slider.setMaxValue(v);
65681         return this;    
65682     },
65683     
65684     /**
65685      * Sets the value for this field.
65686      * @param {Number} v The new value.
65687      * @param {Boolean} animate (optional) Whether to animate the transition. If not specified, it will default to the animate config.
65688      * @return {Ext.form.SliderField} this
65689      */
65690     setValue : function(v, animate, /* private */ silent){
65691         // silent is used if the setValue method is invoked by the slider
65692         // which means we don't need to set the value on the slider.
65693         if(!silent){
65694             this.slider.setValue(v, animate);
65695         }
65696         return Ext.form.SliderField.superclass.setValue.call(this, this.slider.getValue());
65697     },
65698     
65699     /**
65700      * Gets the current value for this field.
65701      * @return {Number} The current value.
65702      */
65703     getValue : function(){
65704         return this.slider.getValue();    
65705     }
65706 });
65707
65708 Ext.reg('sliderfield', Ext.form.SliderField);/**
65709  * @class Ext.form.Label
65710  * @extends Ext.BoxComponent
65711  * Basic Label field.
65712  * @constructor
65713  * Creates a new Label
65714  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
65715  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
65716  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
65717  * @xtype label
65718  */
65719 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
65720     /**
65721      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
65722      * tags within the label's innerHTML, use the {@link #html} config instead.
65723      */
65724     /**
65725      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
65726      * attribute. If not specified, the attribute will not be added to the label.
65727      */
65728     /**
65729      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
65730      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
65731      */
65732
65733     // private
65734     onRender : function(ct, position){
65735         if(!this.el){
65736             this.el = document.createElement('label');
65737             this.el.id = this.getId();
65738             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
65739             if(this.forId){
65740                 this.el.setAttribute('for', this.forId);
65741             }
65742         }
65743         Ext.form.Label.superclass.onRender.call(this, ct, position);
65744     },
65745
65746     /**
65747      * Updates the label's innerHTML with the specified string.
65748      * @param {String} text The new label text
65749      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
65750      * to the label (defaults to true which encodes the value). This might be useful if you want to include
65751      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
65752      * @return {Label} this
65753      */
65754     setText : function(t, encode){
65755         var e = encode === false;
65756         this[!e ? 'text' : 'html'] = t;
65757         delete this[e ? 'text' : 'html'];
65758         if(this.rendered){
65759             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
65760         }
65761         return this;
65762     }
65763 });
65764
65765 Ext.reg('label', Ext.form.Label);/**
65766  * @class Ext.form.Action
65767  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
65768  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
65769  * the Form needs to perform an action such as submit or load. The Configuration options
65770  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
65771  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
65772  * <p>The instance of Action which performed the action is passed to the success
65773  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
65774  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
65775  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
65776  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
65777  */
65778 Ext.form.Action = function(form, options){
65779     this.form = form;
65780     this.options = options || {};
65781 };
65782
65783 /**
65784  * Failure type returned when client side validation of the Form fails
65785  * thus aborting a submit action. Client side validation is performed unless
65786  * {@link #clientValidation} is explicitly set to <tt>false</tt>.
65787  * @type {String}
65788  * @static
65789  */
65790 Ext.form.Action.CLIENT_INVALID = 'client';
65791 /**
65792  * <p>Failure type returned when server side processing fails and the {@link #result}'s
65793  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
65794  * <p>In the case of a form submission, field-specific error messages may be returned in the
65795  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
65796  * @type {String}
65797  * @static
65798  */
65799 Ext.form.Action.SERVER_INVALID = 'server';
65800 /**
65801  * Failure type returned when a communication error happens when attempting
65802  * to send a request to the remote server. The {@link #response} may be examined to
65803  * provide further information.
65804  * @type {String}
65805  * @static
65806  */
65807 Ext.form.Action.CONNECT_FAILURE = 'connect';
65808 /**
65809  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
65810  * property is set to <tt>false</tt>, or no field values are returned in the response's
65811  * <tt style="font-weight:bold">data</tt> property.
65812  * @type {String}
65813  * @static
65814  */
65815 Ext.form.Action.LOAD_FAILURE = 'load';
65816
65817 Ext.form.Action.prototype = {
65818 /**
65819  * @cfg {String} url The URL that the Action is to invoke.
65820  */
65821 /**
65822  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
65823  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
65824  * <b>before</b> the {@link #success} callback is called and before the Form's
65825  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
65826  */
65827 /**
65828  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
65829  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
65830  */
65831 /**
65832  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
65833  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
65834  * input fields.</p>
65835  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
65836  */
65837 /**
65838  * @cfg {Number} timeout The number of seconds to wait for a server response before
65839  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
65840  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
65841  * {@link Ext.form.BasicForm form}.
65842  */
65843 /**
65844  * @cfg {Function} success The function to call when a valid success return packet is recieved.
65845  * The function is passed the following parameters:<ul class="mdetail-params">
65846  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
65847  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
65848  * property of this object may be examined to perform custom postprocessing.</div></li>
65849  * </ul>
65850  */
65851 /**
65852  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
65853  * error ocurred in the Ajax communication.
65854  * The function is passed the following parameters:<ul class="mdetail-params">
65855  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
65856  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
65857  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
65858  * property of this object may be examined to perform custom postprocessing.</div></li>
65859  * </ul>
65860  */
65861 /**
65862  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
65863  * for the callback functions).
65864  */
65865 /**
65866  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
65867  * during the time the action is being processed.
65868  */
65869 /**
65870  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
65871  * during the time the action is being processed.
65872  */
65873
65874 /**
65875  * @cfg {Boolean} submitEmptyText If set to <tt>true</tt>, the emptyText value will be sent with the form
65876  * when it is submitted.  Defaults to <tt>true</tt>.
65877  */
65878
65879 /**
65880  * The type of action this Action instance performs.
65881  * Currently only "submit" and "load" are supported.
65882  * @type {String}
65883  */
65884     type : 'default',
65885 /**
65886  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
65887  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:
65888  * <pre><code>
65889 var fp = new Ext.form.FormPanel({
65890 ...
65891 buttons: [{
65892     text: 'Save',
65893     formBind: true,
65894     handler: function(){
65895         if(fp.getForm().isValid()){
65896             fp.getForm().submit({
65897                 url: 'form-submit.php',
65898                 waitMsg: 'Submitting your data...',
65899                 success: function(form, action){
65900                     // server responded with success = true
65901                     var result = action.{@link #result};
65902                 },
65903                 failure: function(form, action){
65904                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
65905                         Ext.Msg.alert('Error',
65906                             'Status:'+action.{@link #response}.status+': '+
65907                             action.{@link #response}.statusText);
65908                     }
65909                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
65910                         // server responded with success = false
65911                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
65912                     }
65913                 }
65914             });
65915         }
65916     }
65917 },{
65918     text: 'Reset',
65919     handler: function(){
65920         fp.getForm().reset();
65921     }
65922 }]
65923  * </code></pre>
65924  * @property failureType
65925  * @type {String}
65926  */
65927  /**
65928  * The XMLHttpRequest object used to perform the action.
65929  * @property response
65930  * @type {Object}
65931  */
65932  /**
65933  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
65934  * other, action-specific properties.
65935  * @property result
65936  * @type {Object}
65937  */
65938
65939     // interface method
65940     run : function(options){
65941
65942     },
65943
65944     // interface method
65945     success : function(response){
65946
65947     },
65948
65949     // interface method
65950     handleResponse : function(response){
65951
65952     },
65953
65954     // default connection failure
65955     failure : function(response){
65956         this.response = response;
65957         this.failureType = Ext.form.Action.CONNECT_FAILURE;
65958         this.form.afterAction(this, false);
65959     },
65960
65961     // private
65962     // shared code among all Actions to validate that there was a response
65963     // with either responseText or responseXml
65964     processResponse : function(response){
65965         this.response = response;
65966         if(!response.responseText && !response.responseXML){
65967             return true;
65968         }
65969         this.result = this.handleResponse(response);
65970         return this.result;
65971     },
65972
65973     // utility functions used internally
65974     getUrl : function(appendParams){
65975         var url = this.options.url || this.form.url || this.form.el.dom.action;
65976         if(appendParams){
65977             var p = this.getParams();
65978             if(p){
65979                 url = Ext.urlAppend(url, p);
65980             }
65981         }
65982         return url;
65983     },
65984
65985     // private
65986     getMethod : function(){
65987         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
65988     },
65989
65990     // private
65991     getParams : function(){
65992         var bp = this.form.baseParams;
65993         var p = this.options.params;
65994         if(p){
65995             if(typeof p == "object"){
65996                 p = Ext.urlEncode(Ext.applyIf(p, bp));
65997             }else if(typeof p == 'string' && bp){
65998                 p += '&' + Ext.urlEncode(bp);
65999             }
66000         }else if(bp){
66001             p = Ext.urlEncode(bp);
66002         }
66003         return p;
66004     },
66005
66006     // private
66007     createCallback : function(opts){
66008         var opts = opts || {};
66009         return {
66010             success: this.success,
66011             failure: this.failure,
66012             scope: this,
66013             timeout: (opts.timeout*1000) || (this.form.timeout*1000),
66014             upload: this.form.fileUpload ? this.success : undefined
66015         };
66016     }
66017 };
66018
66019 /**
66020  * @class Ext.form.Action.Submit
66021  * @extends Ext.form.Action
66022  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
66023  * and processes the returned response.</p>
66024  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
66025  * {@link Ext.form.BasicForm#submit submit}ting.</p>
66026  * <p><u><b>Response Packet Criteria</b></u></p>
66027  * <p>A response packet may contain:
66028  * <div class="mdetail-params"><ul>
66029  * <li><b><code>success</code></b> property : Boolean
66030  * <div class="sub-desc">The <code>success</code> property is required.</div></li>
66031  * <li><b><code>errors</code></b> property : Object
66032  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
66033  * which is optional, contains error messages for invalid fields.</div></li>
66034  * </ul></div>
66035  * <p><u><b>JSON Packets</b></u></p>
66036  * <p>By default, response packets are assumed to be JSON, so a typical response
66037  * packet may look like this:</p><pre><code>
66038 {
66039     success: false,
66040     errors: {
66041         clientCode: "Client not found",
66042         portOfLoading: "This field must not be null"
66043     }
66044 }</code></pre>
66045  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
66046  * or event handler methods. The object decoded from this JSON is available in the
66047  * {@link Ext.form.Action#result result} property.</p>
66048  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
66049     errorReader: new Ext.data.XmlReader({
66050             record : 'field',
66051             success: '@success'
66052         }, [
66053             'id', 'msg'
66054         ]
66055     )
66056 </code></pre>
66057  * <p>then the results may be sent back in XML format:</p><pre><code>
66058 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
66059 &lt;message success="false"&gt;
66060 &lt;errors&gt;
66061     &lt;field&gt;
66062         &lt;id&gt;clientCode&lt;/id&gt;
66063         &lt;msg&gt;&lt;![CDATA[Code not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
66064     &lt;/field&gt;
66065     &lt;field&gt;
66066         &lt;id&gt;portOfLoading&lt;/id&gt;
66067         &lt;msg&gt;&lt;![CDATA[Port not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
66068     &lt;/field&gt;
66069 &lt;/errors&gt;
66070 &lt;/message&gt;
66071 </code></pre>
66072  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
66073  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
66074  */
66075 Ext.form.Action.Submit = function(form, options){
66076     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
66077 };
66078
66079 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
66080     /**
66081      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
66082      * no need for an errorReader.</b></p>
66083      * <p>A Reader which reads a single record from the returned data. The DataReader's
66084      * <b>success</b> property specifies how submission success is determined. The Record's
66085      * data provides the error messages to apply to any invalid form Fields.</p>
66086      */
66087     /**
66088      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
66089      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
66090      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
66091      * is performed.
66092      */
66093     type : 'submit',
66094
66095     // private
66096     run : function(){
66097         var o = this.options,
66098             method = this.getMethod(),
66099             isGet = method == 'GET';
66100         if(o.clientValidation === false || this.form.isValid()){
66101             if (o.submitEmptyText === false) {
66102                 var fields = this.form.items,
66103                     emptyFields = [];
66104                 fields.each(function(f) {
66105                     if (f.el.getValue() == f.emptyText) {
66106                         emptyFields.push(f);
66107                         f.el.dom.value = "";
66108                     }
66109                 });
66110             }
66111             Ext.Ajax.request(Ext.apply(this.createCallback(o), {
66112                 form:this.form.el.dom,
66113                 url:this.getUrl(isGet),
66114                 method: method,
66115                 headers: o.headers,
66116                 params:!isGet ? this.getParams() : null,
66117                 isUpload: this.form.fileUpload
66118             }));
66119             if (o.submitEmptyText === false) {
66120                 Ext.each(emptyFields, function(f) {
66121                     if (f.applyEmptyText) {
66122                         f.applyEmptyText();
66123                     }
66124                 });
66125             }
66126         }else if (o.clientValidation !== false){ // client validation failed
66127             this.failureType = Ext.form.Action.CLIENT_INVALID;
66128             this.form.afterAction(this, false);
66129         }
66130     },
66131
66132     // private
66133     success : function(response){
66134         var result = this.processResponse(response);
66135         if(result === true || result.success){
66136             this.form.afterAction(this, true);
66137             return;
66138         }
66139         if(result.errors){
66140             this.form.markInvalid(result.errors);
66141         }
66142         this.failureType = Ext.form.Action.SERVER_INVALID;
66143         this.form.afterAction(this, false);
66144     },
66145
66146     // private
66147     handleResponse : function(response){
66148         if(this.form.errorReader){
66149             var rs = this.form.errorReader.read(response);
66150             var errors = [];
66151             if(rs.records){
66152                 for(var i = 0, len = rs.records.length; i < len; i++) {
66153                     var r = rs.records[i];
66154                     errors[i] = r.data;
66155                 }
66156             }
66157             if(errors.length < 1){
66158                 errors = null;
66159             }
66160             return {
66161                 success : rs.success,
66162                 errors : errors
66163             };
66164         }
66165         return Ext.decode(response.responseText);
66166     }
66167 });
66168
66169
66170 /**
66171  * @class Ext.form.Action.Load
66172  * @extends Ext.form.Action
66173  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
66174  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
66175  * {@link Ext.form.BasicForm#load load}ing.</p>
66176  * <p><u><b>Response Packet Criteria</b></u></p>
66177  * <p>A response packet <b>must</b> contain:
66178  * <div class="mdetail-params"><ul>
66179  * <li><b><code>success</code></b> property : Boolean</li>
66180  * <li><b><code>data</code></b> property : Object</li>
66181  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
66182  * The individual value object for each Field is passed to the Field's
66183  * {@link Ext.form.Field#setValue setValue} method.</div></li>
66184  * </ul></div>
66185  * <p><u><b>JSON Packets</b></u></p>
66186  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
66187 var myFormPanel = new Ext.form.FormPanel({
66188     title: 'Client and routing info',
66189     items: [{
66190         fieldLabel: 'Client',
66191         name: 'clientName'
66192     }, {
66193         fieldLabel: 'Port of loading',
66194         name: 'portOfLoading'
66195     }, {
66196         fieldLabel: 'Port of discharge',
66197         name: 'portOfDischarge'
66198     }]
66199 });
66200 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
66201     url: '/getRoutingInfo.php',
66202     params: {
66203         consignmentRef: myConsignmentRef
66204     },
66205     failure: function(form, action) {
66206         Ext.Msg.alert("Load failed", action.result.errorMessage);
66207     }
66208 });
66209 </code></pre>
66210  * a <b>success response</b> packet may look like this:</p><pre><code>
66211 {
66212     success: true,
66213     data: {
66214         clientName: "Fred. Olsen Lines",
66215         portOfLoading: "FXT",
66216         portOfDischarge: "OSL"
66217     }
66218 }</code></pre>
66219  * while a <b>failure response</b> packet may look like this:</p><pre><code>
66220 {
66221     success: false,
66222     errorMessage: "Consignment reference not found"
66223 }</code></pre>
66224  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
66225  * callback or event handler methods. The object decoded from this JSON is available in the
66226  * {@link Ext.form.Action#result result} property.</p>
66227  */
66228 Ext.form.Action.Load = function(form, options){
66229     Ext.form.Action.Load.superclass.constructor.call(this, form, options);
66230     this.reader = this.form.reader;
66231 };
66232
66233 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
66234     // private
66235     type : 'load',
66236
66237     // private
66238     run : function(){
66239         Ext.Ajax.request(Ext.apply(
66240                 this.createCallback(this.options), {
66241                     method:this.getMethod(),
66242                     url:this.getUrl(false),
66243                     headers: this.options.headers,
66244                     params:this.getParams()
66245         }));
66246     },
66247
66248     // private
66249     success : function(response){
66250         var result = this.processResponse(response);
66251         if(result === true || !result.success || !result.data){
66252             this.failureType = Ext.form.Action.LOAD_FAILURE;
66253             this.form.afterAction(this, false);
66254             return;
66255         }
66256         this.form.clearInvalid();
66257         this.form.setValues(result.data);
66258         this.form.afterAction(this, true);
66259     },
66260
66261     // private
66262     handleResponse : function(response){
66263         if(this.form.reader){
66264             var rs = this.form.reader.read(response);
66265             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
66266             return {
66267                 success : rs.success,
66268                 data : data
66269             };
66270         }
66271         return Ext.decode(response.responseText);
66272     }
66273 });
66274
66275
66276
66277 /**
66278  * @class Ext.form.Action.DirectLoad
66279  * @extends Ext.form.Action.Load
66280  * <p>Provides Ext.direct support for loading form data.</p>
66281  * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
66282  * <pre><code>
66283 var myFormPanel = new Ext.form.FormPanel({
66284     // configs for FormPanel
66285     title: 'Basic Information',
66286     renderTo: document.body,
66287     width: 300, height: 160,
66288     padding: 10,
66289
66290     // configs apply to child items
66291     defaults: {anchor: '100%'},
66292     defaultType: 'textfield',
66293     items: [{
66294         fieldLabel: 'Name',
66295         name: 'name'
66296     },{
66297         fieldLabel: 'Email',
66298         name: 'email'
66299     },{
66300         fieldLabel: 'Company',
66301         name: 'company'
66302     }],
66303
66304     // configs for BasicForm
66305     api: {
66306         // The server-side method to call for load() requests
66307         load: Profile.getBasicInfo,
66308         // The server-side must mark the submit handler as a 'formHandler'
66309         submit: Profile.updateBasicInfo
66310     },
66311     // specify the order for the passed params
66312     paramOrder: ['uid', 'foo']
66313 });
66314
66315 // load the form
66316 myFormPanel.getForm().load({
66317     // pass 2 arguments to server side getBasicInfo method (len=2)
66318     params: {
66319         foo: 'bar',
66320         uid: 34
66321     }
66322 });
66323  * </code></pre>
66324  * The data packet sent to the server will resemble something like:
66325  * <pre><code>
66326 [
66327     {
66328         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
66329         "data":[34,"bar"] // note the order of the params
66330     }
66331 ]
66332  * </code></pre>
66333  * The form will process a data packet returned by the server that is similar
66334  * to the following format:
66335  * <pre><code>
66336 [
66337     {
66338         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
66339         "result":{
66340             "success":true,
66341             "data":{
66342                 "name":"Fred Flintstone",
66343                 "company":"Slate Rock and Gravel",
66344                 "email":"fred.flintstone@slaterg.com"
66345             }
66346         }
66347     }
66348 ]
66349  * </code></pre>
66350  */
66351 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
66352     constructor: function(form, opts) {
66353         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
66354     },
66355     type : 'directload',
66356
66357     run : function(){
66358         var args = this.getParams();
66359         args.push(this.success, this);
66360         this.form.api.load.apply(window, args);
66361     },
66362
66363     getParams : function() {
66364         var buf = [], o = {};
66365         var bp = this.form.baseParams;
66366         var p = this.options.params;
66367         Ext.apply(o, p, bp);
66368         var paramOrder = this.form.paramOrder;
66369         if(paramOrder){
66370             for(var i = 0, len = paramOrder.length; i < len; i++){
66371                 buf.push(o[paramOrder[i]]);
66372             }
66373         }else if(this.form.paramsAsHash){
66374             buf.push(o);
66375         }
66376         return buf;
66377     },
66378     // Direct actions have already been processed and therefore
66379     // we can directly set the result; Direct Actions do not have
66380     // a this.response property.
66381     processResponse : function(result) {
66382         this.result = result;
66383         return result;
66384     },
66385
66386     success : function(response, trans){
66387         if(trans.type == Ext.Direct.exceptions.SERVER){
66388             response = {};
66389         }
66390         Ext.form.Action.DirectLoad.superclass.success.call(this, response);
66391     }
66392 });
66393
66394 /**
66395  * @class Ext.form.Action.DirectSubmit
66396  * @extends Ext.form.Action.Submit
66397  * <p>Provides Ext.direct support for submitting form data.</p>
66398  * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
66399  * <pre><code>
66400 var myFormPanel = new Ext.form.FormPanel({
66401     // configs for FormPanel
66402     title: 'Basic Information',
66403     renderTo: document.body,
66404     width: 300, height: 160,
66405     padding: 10,
66406     buttons:[{
66407         text: 'Submit',
66408         handler: function(){
66409             myFormPanel.getForm().submit({
66410                 params: {
66411                     foo: 'bar',
66412                     uid: 34
66413                 }
66414             });
66415         }
66416     }],
66417
66418     // configs apply to child items
66419     defaults: {anchor: '100%'},
66420     defaultType: 'textfield',
66421     items: [{
66422         fieldLabel: 'Name',
66423         name: 'name'
66424     },{
66425         fieldLabel: 'Email',
66426         name: 'email'
66427     },{
66428         fieldLabel: 'Company',
66429         name: 'company'
66430     }],
66431
66432     // configs for BasicForm
66433     api: {
66434         // The server-side method to call for load() requests
66435         load: Profile.getBasicInfo,
66436         // The server-side must mark the submit handler as a 'formHandler'
66437         submit: Profile.updateBasicInfo
66438     },
66439     // specify the order for the passed params
66440     paramOrder: ['uid', 'foo']
66441 });
66442  * </code></pre>
66443  * The data packet sent to the server will resemble something like:
66444  * <pre><code>
66445 {
66446     "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
66447     "result":{
66448         "success":true,
66449         "id":{
66450             "extAction":"Profile","extMethod":"updateBasicInfo",
66451             "extType":"rpc","extTID":"6","extUpload":"false",
66452             "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
66453         }
66454     }
66455 }
66456  * </code></pre>
66457  * The form will process a data packet returned by the server that is similar
66458  * to the following:
66459  * <pre><code>
66460 // sample success packet (batched requests)
66461 [
66462     {
66463         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
66464         "result":{
66465             "success":true
66466         }
66467     }
66468 ]
66469
66470 // sample failure packet (one request)
66471 {
66472         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
66473         "result":{
66474             "errors":{
66475                 "email":"already taken"
66476             },
66477             "success":false,
66478             "foo":"bar"
66479         }
66480 }
66481  * </code></pre>
66482  * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
66483  */
66484 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
66485     constructor : function(form, opts) {
66486         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
66487     },
66488     type : 'directsubmit',
66489     // override of Submit
66490     run : function(){
66491         var o = this.options;
66492         if(o.clientValidation === false || this.form.isValid()){
66493             // tag on any additional params to be posted in the
66494             // form scope
66495             this.success.params = this.getParams();
66496             this.form.api.submit(this.form.el.dom, this.success, this);
66497         }else if (o.clientValidation !== false){ // client validation failed
66498             this.failureType = Ext.form.Action.CLIENT_INVALID;
66499             this.form.afterAction(this, false);
66500         }
66501     },
66502
66503     getParams : function() {
66504         var o = {};
66505         var bp = this.form.baseParams;
66506         var p = this.options.params;
66507         Ext.apply(o, p, bp);
66508         return o;
66509     },
66510     // Direct actions have already been processed and therefore
66511     // we can directly set the result; Direct Actions do not have
66512     // a this.response property.
66513     processResponse : function(result) {
66514         this.result = result;
66515         return result;
66516     },
66517
66518     success : function(response, trans){
66519         if(trans.type == Ext.Direct.exceptions.SERVER){
66520             response = {};
66521         }
66522         Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
66523     }
66524 });
66525
66526 Ext.form.Action.ACTION_TYPES = {
66527     'load' : Ext.form.Action.Load,
66528     'submit' : Ext.form.Action.Submit,
66529     'directload' : Ext.form.Action.DirectLoad,
66530     'directsubmit' : Ext.form.Action.DirectSubmit
66531 };
66532 /**
66533  * @class Ext.form.VTypes
66534  * <p>This is a singleton object which contains a set of commonly used field validation functions.
66535  * The validations provided are basic and intended to be easily customizable and extended.</p>
66536  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
66537  * test function, and optionally specify any corresponding error text to display and any keystroke
66538  * filtering mask to apply. For example:</p>
66539  * <pre><code>
66540 // custom Vtype for vtype:'time'
66541 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
66542 Ext.apply(Ext.form.VTypes, {
66543     //  vtype validation function
66544     time: function(val, field) {
66545         return timeTest.test(val);
66546     },
66547     // vtype Text property: The error text to display when the validation function returns false
66548     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
66549     // vtype Mask property: The keystroke filter mask
66550     timeMask: /[\d\s:amp]/i
66551 });
66552  * </code></pre>
66553  * Another example: 
66554  * <pre><code>
66555 // custom Vtype for vtype:'IPAddress'
66556 Ext.apply(Ext.form.VTypes, {
66557     IPAddress:  function(v) {
66558         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
66559     },
66560     IPAddressText: 'Must be a numeric IP address',
66561     IPAddressMask: /[\d\.]/i
66562 });
66563  * </code></pre>
66564  * @singleton
66565  */
66566 Ext.form.VTypes = function(){
66567     // closure these in so they are only created once.
66568     var alpha = /^[a-zA-Z_]+$/,
66569         alphanum = /^[a-zA-Z0-9_]+$/,
66570         email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
66571         url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
66572
66573     // All these messages and functions are configurable
66574     return {
66575         /**
66576          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
66577          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
66578          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
66579          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a> 
66580          * for additional information.  This implementation is intended to validate the following emails:<tt>
66581          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
66582          * </tt>.
66583          * @param {String} value The email address
66584          * @return {Boolean} true if the RegExp test passed, and false if not.
66585          */
66586         'email' : function(v){
66587             return email.test(v);
66588         },
66589         /**
66590          * The error text to display when the email validation function returns false.  Defaults to:
66591          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
66592          * @type String
66593          */
66594         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
66595         /**
66596          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for 
66597          * information about more complex email validation. Defaults to:
66598          * <tt>/[a-z0-9_\.\-@]/i</tt>
66599          * @type RegExp
66600          */
66601         'emailMask' : /[a-z0-9_\.\-@]/i,
66602
66603         /**
66604          * The function used to validate URLs
66605          * @param {String} value The URL
66606          * @return {Boolean} true if the RegExp test passed, and false if not.
66607          */
66608         'url' : function(v){
66609             return url.test(v);
66610         },
66611         /**
66612          * The error text to display when the url validation function returns false.  Defaults to:
66613          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
66614          * @type String
66615          */
66616         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
66617         
66618         /**
66619          * The function used to validate alpha values
66620          * @param {String} value The value
66621          * @return {Boolean} true if the RegExp test passed, and false if not.
66622          */
66623         'alpha' : function(v){
66624             return alpha.test(v);
66625         },
66626         /**
66627          * The error text to display when the alpha validation function returns false.  Defaults to:
66628          * <tt>'This field should only contain letters and _'</tt>
66629          * @type String
66630          */
66631         'alphaText' : 'This field should only contain letters and _',
66632         /**
66633          * The keystroke filter mask to be applied on alpha input.  Defaults to:
66634          * <tt>/[a-z_]/i</tt>
66635          * @type RegExp
66636          */
66637         'alphaMask' : /[a-z_]/i,
66638
66639         /**
66640          * The function used to validate alphanumeric values
66641          * @param {String} value The value
66642          * @return {Boolean} true if the RegExp test passed, and false if not.
66643          */
66644         'alphanum' : function(v){
66645             return alphanum.test(v);
66646         },
66647         /**
66648          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
66649          * <tt>'This field should only contain letters, numbers and _'</tt>
66650          * @type String
66651          */
66652         'alphanumText' : 'This field should only contain letters, numbers and _',
66653         /**
66654          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
66655          * <tt>/[a-z0-9_]/i</tt>
66656          * @type RegExp
66657          */
66658         'alphanumMask' : /[a-z0-9_]/i
66659     };
66660 }();/**
66661  * @class Ext.grid.GridPanel
66662  * @extends Ext.Panel
66663  * <p>This class represents the primary interface of a component based grid control to represent data
66664  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>
66665  * <div class="mdetail-params"><ul>
66666  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)
66667  * <div class="sub-desc"></div></li>
66668  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup
66669  * <div class="sub-desc"></div></li>
66670  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface
66671  * <div class="sub-desc"></div></li>
66672  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior
66673  * <div class="sub-desc"></div></li>
66674  * </ul></div>
66675  * <p>Example usage:</p>
66676  * <pre><code>
66677 var grid = new Ext.grid.GridPanel({
66678     {@link #store}: new {@link Ext.data.Store}({
66679         {@link Ext.data.Store#autoDestroy autoDestroy}: true,
66680         {@link Ext.data.Store#reader reader}: reader,
66681         {@link Ext.data.Store#data data}: xg.dummyData
66682     }),
66683     {@link #colModel}: new {@link Ext.grid.ColumnModel}({
66684         {@link Ext.grid.ColumnModel#defaults defaults}: {
66685             width: 120,
66686             sortable: true
66687         },
66688         {@link Ext.grid.ColumnModel#columns columns}: [
66689             {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},
66690             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
66691             {header: 'Change', dataIndex: 'change'},
66692             {header: '% Change', dataIndex: 'pctChange'},
66693             // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype
66694             {
66695                 header: 'Last Updated', width: 135, dataIndex: 'lastChange',
66696                 xtype: 'datecolumn', format: 'M d, Y'
66697             }
66698         ],
66699     }),
66700     {@link #viewConfig}: {
66701         {@link Ext.grid.GridView#forceFit forceFit}: true,
66702
66703 //      Return CSS class to apply to rows depending upon data values
66704         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {
66705             var c = record.{@link Ext.data.Record#get get}('change');
66706             if (c < 0) {
66707                 return 'price-fall';
66708             } else if (c > 0) {
66709                 return 'price-rise';
66710             }
66711         }
66712     },
66713     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),
66714     width: 600,
66715     height: 300,
66716     frame: true,
66717     title: 'Framed with Row Selection and Horizontal Scrolling',
66718     iconCls: 'icon-grid'
66719 });
66720  * </code></pre>
66721  * <p><b><u>Notes:</u></b></p>
66722  * <div class="mdetail-params"><ul>
66723  * <li>Although this class inherits many configuration options from base classes, some of them
66724  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will
66725  * have no effect.</li>
66726  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to
66727  * scroll its rows. These dimensions can either be set explicitly through the
66728  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>
66729  * configuration options or implicitly set by using the grid as a child item of a
66730  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}
66731  * provide the sizing of its child items (for example the Container of the Grid may specify
66732  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>
66733  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated
66734  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>
66735  * </ul></div>
66736  * @constructor
66737  * @param {Object} config The config object
66738  * @xtype grid
66739  */
66740 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {
66741     /**
66742      * @cfg {String} autoExpandColumn
66743      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in
66744      * this grid that should expand to fill unused space. This value specified here can not
66745      * be <tt>0</tt>.</p>
66746      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with
66747      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>
66748      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>
66749      * for additional details.</p>
66750      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>
66751      */
66752     autoExpandColumn : false,
66753     /**
66754      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>
66755      * can have (if enabled). Defaults to <tt>1000</tt>.
66756      */
66757     autoExpandMax : 1000,
66758     /**
66759      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>
66760      * can have (if enabled). Defaults to <tt>50</tt>.
66761      */
66762     autoExpandMin : 50,
66763     /**
66764      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.
66765      * Default is <tt>false</tt>.
66766      */
66767     columnLines : false,
66768     /**
66769      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.
66770      */
66771     /**
66772      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).
66773      */
66774     /**
66775      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a
66776      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the
66777      * <tt>{@link #colModel}</tt> configuration property.
66778      */
66779     /**
66780      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.
66781      */
66782     /**
66783      * @cfg {String} ddText
66784      * Configures the text in the drag proxy.  Defaults to:
66785      * <pre><code>
66786      * ddText : '{0} selected row{1}'
66787      * </code></pre>
66788      * <tt>{0}</tt> is replaced with the number of selected rows.
66789      */
66790     ddText : '{0} selected row{1}',
66791     /**
66792      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>
66793      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row
66794      * structure deferred so that layouts with GridPanels appear more quickly.</p>
66795      */
66796     deferRowRender : true,
66797     /**
66798      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>
66799      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>
66800      */
66801     /**
66802      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.
66803      */
66804     /**
66805      * @cfg {Boolean} enableColumnHide
66806      * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}
66807      * with the {@link #enableHdMenu header menu}.
66808      */
66809     enableColumnHide : true,
66810     /**
66811      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>
66812      * to turn off column reordering via drag drop.
66813      */
66814     enableColumnMove : true,
66815     /**
66816      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>
66817      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to
66818      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>
66819      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>
66820      * property.</p>
66821      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of
66822      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
66823      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able
66824      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>
66825      */
66826     enableDragDrop : false,
66827     /**
66828      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.
66829      */
66830     enableHdMenu : true,
66831     /**
66832      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.
66833      */
66834     /**
66835      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while
66836      * loading. Defaults to <code>false</code>.
66837      */
66838     loadMask : false,
66839     /**
66840      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.
66841      */
66842     /**
66843      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.
66844      */
66845     minColumnWidth : 25,
66846     /**
66847      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.
66848      */
66849     /**
66850      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide
66851      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).
66852      */
66853     /**
66854      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).
66855      */
66856     /**
66857      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
66858      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of
66859      * the grid. A default CSS rule is provided which sets a background colour, but you can override this
66860      * with a rule which either overrides the <b>background-color</b> style using the '!important'
66861      * modifier, or which uses a CSS selector of higher specificity.</p>
66862      */
66863     stripeRows : false,
66864     /**
66865      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>
66866      * for GridPanel, but <tt>false</tt> for EditorGridPanel.
66867      */
66868     trackMouseOver : true,
66869     /**
66870      * @cfg {Array} stateEvents
66871      * An array of events that, when fired, should trigger this component to save its state.
66872      * Defaults to:<pre><code>
66873      * stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange']
66874      * </code></pre>
66875      * <p>These can be any types of events supported by this component, including browser or
66876      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>
66877      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring
66878      * Component state.</p>
66879      */
66880     stateEvents : ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
66881     /**
66882      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set
66883      * before a call to {@link Ext.Component#render render()}.
66884      */
66885     view : null,
66886
66887     /**
66888      * @cfg {Array} bubbleEvents
66889      * <p>An array of events that, when fired, should be bubbled to any parent container.
66890      * See {@link Ext.util.Observable#enableBubble}.
66891      * Defaults to <tt>[]</tt>.
66892      */
66893     bubbleEvents: [],
66894
66895     /**
66896      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of
66897      * the config options available for {@link Ext.grid.GridView} can be specified here. This option
66898      * is ignored if <tt>{@link #view}</tt> is specified.
66899      */
66900
66901     // private
66902     rendered : false,
66903     // private
66904     viewReady : false,
66905
66906     // private
66907     initComponent : function(){
66908         Ext.grid.GridPanel.superclass.initComponent.call(this);
66909
66910         if(this.columnLines){
66911             this.cls = (this.cls || '') + ' x-grid-with-col-lines';
66912         }
66913         // override any provided value since it isn't valid
66914         // and is causing too many bug reports ;)
66915         this.autoScroll = false;
66916         this.autoWidth = false;
66917
66918         if(Ext.isArray(this.columns)){
66919             this.colModel = new Ext.grid.ColumnModel(this.columns);
66920             delete this.columns;
66921         }
66922
66923         // check and correct shorthanded configs
66924         if(this.ds){
66925             this.store = this.ds;
66926             delete this.ds;
66927         }
66928         if(this.cm){
66929             this.colModel = this.cm;
66930             delete this.cm;
66931         }
66932         if(this.sm){
66933             this.selModel = this.sm;
66934             delete this.sm;
66935         }
66936         this.store = Ext.StoreMgr.lookup(this.store);
66937
66938         this.addEvents(
66939             // raw events
66940             /**
66941              * @event click
66942              * The raw click event for the entire grid.
66943              * @param {Ext.EventObject} e
66944              */
66945             'click',
66946             /**
66947              * @event dblclick
66948              * The raw dblclick event for the entire grid.
66949              * @param {Ext.EventObject} e
66950              */
66951             'dblclick',
66952             /**
66953              * @event contextmenu
66954              * The raw contextmenu event for the entire grid.
66955              * @param {Ext.EventObject} e
66956              */
66957             'contextmenu',
66958             /**
66959              * @event mousedown
66960              * The raw mousedown event for the entire grid.
66961              * @param {Ext.EventObject} e
66962              */
66963             'mousedown',
66964             /**
66965              * @event mouseup
66966              * The raw mouseup event for the entire grid.
66967              * @param {Ext.EventObject} e
66968              */
66969             'mouseup',
66970             /**
66971              * @event mouseover
66972              * The raw mouseover event for the entire grid.
66973              * @param {Ext.EventObject} e
66974              */
66975             'mouseover',
66976             /**
66977              * @event mouseout
66978              * The raw mouseout event for the entire grid.
66979              * @param {Ext.EventObject} e
66980              */
66981             'mouseout',
66982             /**
66983              * @event keypress
66984              * The raw keypress event for the entire grid.
66985              * @param {Ext.EventObject} e
66986              */
66987             'keypress',
66988             /**
66989              * @event keydown
66990              * The raw keydown event for the entire grid.
66991              * @param {Ext.EventObject} e
66992              */
66993             'keydown',
66994
66995             // custom events
66996             /**
66997              * @event cellmousedown
66998              * Fires before a cell is clicked
66999              * @param {Grid} this
67000              * @param {Number} rowIndex
67001              * @param {Number} columnIndex
67002              * @param {Ext.EventObject} e
67003              */
67004             'cellmousedown',
67005             /**
67006              * @event rowmousedown
67007              * Fires before a row is clicked
67008              * @param {Grid} this
67009              * @param {Number} rowIndex
67010              * @param {Ext.EventObject} e
67011              */
67012             'rowmousedown',
67013             /**
67014              * @event headermousedown
67015              * Fires before a header is clicked
67016              * @param {Grid} this
67017              * @param {Number} columnIndex
67018              * @param {Ext.EventObject} e
67019              */
67020             'headermousedown',
67021
67022             /**
67023              * @event groupmousedown
67024              * Fires before a group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67025              * @param {Grid} this
67026              * @param {String} groupField
67027              * @param {String} groupValue
67028              * @param {Ext.EventObject} e
67029              */
67030             'groupmousedown',
67031
67032             /**
67033              * @event rowbodymousedown
67034              * Fires before the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67035              * @param {Grid} this
67036              * @param {Number} rowIndex
67037              * @param {Ext.EventObject} e
67038              */
67039             'rowbodymousedown',
67040
67041             /**
67042              * @event containermousedown
67043              * Fires before the container is clicked. The container consists of any part of the grid body that is not covered by a row.
67044              * @param {Grid} this
67045              * @param {Ext.EventObject} e
67046              */
67047             'containermousedown',
67048
67049             /**
67050              * @event cellclick
67051              * Fires when a cell is clicked.
67052              * The data for the cell is drawn from the {@link Ext.data.Record Record}
67053              * for this row. To access the data in the listener function use the
67054              * following technique:
67055              * <pre><code>
67056 function(grid, rowIndex, columnIndex, e) {
67057     var record = grid.getStore().getAt(rowIndex);  // Get the Record
67058     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
67059     var data = record.get(fieldName);
67060 }
67061 </code></pre>
67062              * @param {Grid} this
67063              * @param {Number} rowIndex
67064              * @param {Number} columnIndex
67065              * @param {Ext.EventObject} e
67066              */
67067             'cellclick',
67068             /**
67069              * @event celldblclick
67070              * Fires when a cell is double clicked
67071              * @param {Grid} this
67072              * @param {Number} rowIndex
67073              * @param {Number} columnIndex
67074              * @param {Ext.EventObject} e
67075              */
67076             'celldblclick',
67077             /**
67078              * @event rowclick
67079              * Fires when a row is clicked
67080              * @param {Grid} this
67081              * @param {Number} rowIndex
67082              * @param {Ext.EventObject} e
67083              */
67084             'rowclick',
67085             /**
67086              * @event rowdblclick
67087              * Fires when a row is double clicked
67088              * @param {Grid} this
67089              * @param {Number} rowIndex
67090              * @param {Ext.EventObject} e
67091              */
67092             'rowdblclick',
67093             /**
67094              * @event headerclick
67095              * Fires when a header is clicked
67096              * @param {Grid} this
67097              * @param {Number} columnIndex
67098              * @param {Ext.EventObject} e
67099              */
67100             'headerclick',
67101             /**
67102              * @event headerdblclick
67103              * Fires when a header cell is double clicked
67104              * @param {Grid} this
67105              * @param {Number} columnIndex
67106              * @param {Ext.EventObject} e
67107              */
67108             'headerdblclick',
67109             /**
67110              * @event groupclick
67111              * Fires when group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67112              * @param {Grid} this
67113              * @param {String} groupField
67114              * @param {String} groupValue
67115              * @param {Ext.EventObject} e
67116              */
67117             'groupclick',
67118             /**
67119              * @event groupdblclick
67120              * Fires when group header is double clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67121              * @param {Grid} this
67122              * @param {String} groupField
67123              * @param {String} groupValue
67124              * @param {Ext.EventObject} e
67125              */
67126             'groupdblclick',
67127             /**
67128              * @event containerclick
67129              * Fires when the container is clicked. The container consists of any part of the grid body that is not covered by a row.
67130              * @param {Grid} this
67131              * @param {Ext.EventObject} e
67132              */
67133             'containerclick',
67134             /**
67135              * @event containerdblclick
67136              * Fires when the container is double clicked. The container consists of any part of the grid body that is not covered by a row.
67137              * @param {Grid} this
67138              * @param {Ext.EventObject} e
67139              */
67140             'containerdblclick',
67141
67142             /**
67143              * @event rowbodyclick
67144              * Fires when the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67145              * @param {Grid} this
67146              * @param {Number} rowIndex
67147              * @param {Ext.EventObject} e
67148              */
67149             'rowbodyclick',
67150             /**
67151              * @event rowbodydblclick
67152              * Fires when the row body is double clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67153              * @param {Grid} this
67154              * @param {Number} rowIndex
67155              * @param {Ext.EventObject} e
67156              */
67157             'rowbodydblclick',
67158
67159             /**
67160              * @event rowcontextmenu
67161              * Fires when a row is right clicked
67162              * @param {Grid} this
67163              * @param {Number} rowIndex
67164              * @param {Ext.EventObject} e
67165              */
67166             'rowcontextmenu',
67167             /**
67168              * @event cellcontextmenu
67169              * Fires when a cell is right clicked
67170              * @param {Grid} this
67171              * @param {Number} rowIndex
67172              * @param {Number} cellIndex
67173              * @param {Ext.EventObject} e
67174              */
67175             'cellcontextmenu',
67176             /**
67177              * @event headercontextmenu
67178              * Fires when a header is right clicked
67179              * @param {Grid} this
67180              * @param {Number} columnIndex
67181              * @param {Ext.EventObject} e
67182              */
67183             'headercontextmenu',
67184             /**
67185              * @event groupcontextmenu
67186              * Fires when group header is right clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67187              * @param {Grid} this
67188              * @param {String} groupField
67189              * @param {String} groupValue
67190              * @param {Ext.EventObject} e
67191              */
67192             'groupcontextmenu',
67193             /**
67194              * @event containercontextmenu
67195              * Fires when the container is right clicked. The container consists of any part of the grid body that is not covered by a row.
67196              * @param {Grid} this
67197              * @param {Ext.EventObject} e
67198              */
67199             'containercontextmenu',
67200             /**
67201              * @event rowbodycontextmenu
67202              * Fires when the row body is right clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67203              * @param {Grid} this
67204              * @param {Number} rowIndex
67205              * @param {Ext.EventObject} e
67206              */
67207             'rowbodycontextmenu',
67208             /**
67209              * @event bodyscroll
67210              * Fires when the body element is scrolled
67211              * @param {Number} scrollLeft
67212              * @param {Number} scrollTop
67213              */
67214             'bodyscroll',
67215             /**
67216              * @event columnresize
67217              * Fires when the user resizes a column
67218              * @param {Number} columnIndex
67219              * @param {Number} newSize
67220              */
67221             'columnresize',
67222             /**
67223              * @event columnmove
67224              * Fires when the user moves a column
67225              * @param {Number} oldIndex
67226              * @param {Number} newIndex
67227              */
67228             'columnmove',
67229             /**
67230              * @event sortchange
67231              * Fires when the grid's store sort changes
67232              * @param {Grid} this
67233              * @param {Object} sortInfo An object with the keys field and direction
67234              */
67235             'sortchange',
67236             /**
67237              * @event groupchange
67238              * Fires when the grid's grouping changes (only applies for grids with a {@link Ext.grid.GroupingView GroupingView})
67239              * @param {Grid} this
67240              * @param {String} groupField A string with the grouping field, null if the store is not grouped.
67241              */
67242             'groupchange',
67243             /**
67244              * @event reconfigure
67245              * Fires when the grid is reconfigured with a new store and/or column model.
67246              * @param {Grid} this
67247              * @param {Ext.data.Store} store The new store
67248              * @param {Ext.grid.ColumnModel} colModel The new column model
67249              */
67250             'reconfigure',
67251             /**
67252              * @event viewready
67253              * Fires when the grid view is available (use this for selecting a default row).
67254              * @param {Grid} this
67255              */
67256             'viewready'
67257         );
67258     },
67259
67260     // private
67261     onRender : function(ct, position){
67262         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);
67263
67264         var c = this.getGridEl();
67265
67266         this.el.addClass('x-grid-panel');
67267
67268         this.mon(c, {
67269             scope: this,
67270             mousedown: this.onMouseDown,
67271             click: this.onClick,
67272             dblclick: this.onDblClick,
67273             contextmenu: this.onContextMenu
67274         });
67275
67276         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);
67277
67278         var view = this.getView();
67279         view.init(this);
67280         view.render();
67281         this.getSelectionModel().init(this);
67282     },
67283
67284     // private
67285     initEvents : function(){
67286         Ext.grid.GridPanel.superclass.initEvents.call(this);
67287
67288         if(this.loadMask){
67289             this.loadMask = new Ext.LoadMask(this.bwrap,
67290                     Ext.apply({store:this.store}, this.loadMask));
67291         }
67292     },
67293
67294     initStateEvents : function(){
67295         Ext.grid.GridPanel.superclass.initStateEvents.call(this);
67296         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});
67297     },
67298
67299     applyState : function(state){
67300         var cm = this.colModel,
67301             cs = state.columns,
67302             store = this.store,
67303             s,
67304             c,
67305             oldIndex;
67306
67307         if(cs){
67308             for(var i = 0, len = cs.length; i < len; i++){
67309                 s = cs[i];
67310                 c = cm.getColumnById(s.id);
67311                 if(c){
67312                     c.hidden = s.hidden;
67313                     c.width = s.width;
67314                     oldIndex = cm.getIndexById(s.id);
67315                     if(oldIndex != i){
67316                         cm.moveColumn(oldIndex, i);
67317                     }
67318                 }
67319             }
67320         }
67321         if(store){
67322             s = state.sort;
67323             if(s){
67324                 store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);
67325             }
67326             s = state.group;
67327             if(store.groupBy){
67328                 if(s){
67329                     store.groupBy(s);
67330                 }else{
67331                     store.clearGrouping();
67332                 }
67333             }
67334
67335         }
67336         var o = Ext.apply({}, state);
67337         delete o.columns;
67338         delete o.sort;
67339         Ext.grid.GridPanel.superclass.applyState.call(this, o);
67340     },
67341
67342     getState : function(){
67343         var o = {columns: []},
67344             store = this.store,
67345             ss,
67346             gs;
67347
67348         for(var i = 0, c; (c = this.colModel.config[i]); i++){
67349             o.columns[i] = {
67350                 id: c.id,
67351                 width: c.width
67352             };
67353             if(c.hidden){
67354                 o.columns[i].hidden = true;
67355             }
67356         }
67357         if(store){
67358             ss = store.getSortState();
67359             if(ss){
67360                 o.sort = ss;
67361             }
67362             if(store.getGroupState){
67363                 gs = store.getGroupState();
67364                 if(gs){
67365                     o.group = gs;
67366                 }
67367             }
67368         }
67369         return o;
67370     },
67371
67372     // private
67373     afterRender : function(){
67374         Ext.grid.GridPanel.superclass.afterRender.call(this);
67375         var v = this.view;
67376         this.on('bodyresize', v.layout, v);
67377         v.layout();
67378         if(this.deferRowRender){
67379             if (!this.deferRowRenderTask){
67380                 this.deferRowRenderTask = new Ext.util.DelayedTask(v.afterRender, this.view);
67381             }
67382             this.deferRowRenderTask.delay(10);
67383         }else{
67384             v.afterRender();
67385         }
67386         this.viewReady = true;
67387     },
67388
67389     /**
67390      * <p>Reconfigures the grid to use a different Store and Column Model
67391      * and fires the 'reconfigure' event. The View will be bound to the new
67392      * objects and refreshed.</p>
67393      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become
67394      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the
67395      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound
67396      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring
67397      * with the new data.</p>
67398      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object
67399      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object
67400      */
67401     reconfigure : function(store, colModel){
67402         var rendered = this.rendered;
67403         if(rendered){
67404             if(this.loadMask){
67405                 this.loadMask.destroy();
67406                 this.loadMask = new Ext.LoadMask(this.bwrap,
67407                         Ext.apply({}, {store:store}, this.initialConfig.loadMask));
67408             }
67409         }
67410         if(this.view){
67411             this.view.initData(store, colModel);
67412         }
67413         this.store = store;
67414         this.colModel = colModel;
67415         if(rendered){
67416             this.view.refresh(true);
67417         }
67418         this.fireEvent('reconfigure', this, store, colModel);
67419     },
67420
67421     // private
67422     onDestroy : function(){
67423         if (this.deferRowRenderTask && this.deferRowRenderTask.cancel){
67424             this.deferRowRenderTask.cancel();
67425         }
67426         if(this.rendered){
67427             Ext.destroy(this.view, this.loadMask);
67428         }else if(this.store && this.store.autoDestroy){
67429             this.store.destroy();
67430         }
67431         Ext.destroy(this.colModel, this.selModel);
67432         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;
67433         Ext.grid.GridPanel.superclass.onDestroy.call(this);
67434     },
67435
67436     // private
67437     processEvent : function(name, e){
67438         this.view.processEvent(name, e);
67439     },
67440
67441     // private
67442     onClick : function(e){
67443         this.processEvent('click', e);
67444     },
67445
67446     // private
67447     onMouseDown : function(e){
67448         this.processEvent('mousedown', e);
67449     },
67450
67451     // private
67452     onContextMenu : function(e, t){
67453         this.processEvent('contextmenu', e);
67454     },
67455
67456     // private
67457     onDblClick : function(e){
67458         this.processEvent('dblclick', e);
67459     },
67460
67461     // private
67462     walkCells : function(row, col, step, fn, scope){
67463         var cm    = this.colModel,
67464             clen  = cm.getColumnCount(),
67465             ds    = this.store,
67466             rlen  = ds.getCount(),
67467             first = true;
67468
67469         if(step < 0){
67470             if(col < 0){
67471                 row--;
67472                 first = false;
67473             }
67474             while(row >= 0){
67475                 if(!first){
67476                     col = clen-1;
67477                 }
67478                 first = false;
67479                 while(col >= 0){
67480                     if(fn.call(scope || this, row, col, cm) === true){
67481                         return [row, col];
67482                     }
67483                     col--;
67484                 }
67485                 row--;
67486             }
67487         } else {
67488             if(col >= clen){
67489                 row++;
67490                 first = false;
67491             }
67492             while(row < rlen){
67493                 if(!first){
67494                     col = 0;
67495                 }
67496                 first = false;
67497                 while(col < clen){
67498                     if(fn.call(scope || this, row, col, cm) === true){
67499                         return [row, col];
67500                     }
67501                     col++;
67502                 }
67503                 row++;
67504             }
67505         }
67506         return null;
67507     },
67508
67509     /**
67510      * Returns the grid's underlying element.
67511      * @return {Element} The element
67512      */
67513     getGridEl : function(){
67514         return this.body;
67515     },
67516
67517     // private for compatibility, overridden by editor grid
67518     stopEditing : Ext.emptyFn,
67519
67520     /**
67521      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>
67522      * configuration option. If no selection model was configured, this will create
67523      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.
67524      * @return {SelectionModel}
67525      */
67526     getSelectionModel : function(){
67527         if(!this.selModel){
67528             this.selModel = new Ext.grid.RowSelectionModel(
67529                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);
67530         }
67531         return this.selModel;
67532     },
67533
67534     /**
67535      * Returns the grid's data store.
67536      * @return {Ext.data.Store} The store
67537      */
67538     getStore : function(){
67539         return this.store;
67540     },
67541
67542     /**
67543      * Returns the grid's ColumnModel.
67544      * @return {Ext.grid.ColumnModel} The column model
67545      */
67546     getColumnModel : function(){
67547         return this.colModel;
67548     },
67549
67550     /**
67551      * Returns the grid's GridView object.
67552      * @return {Ext.grid.GridView} The grid view
67553      */
67554     getView : function(){
67555         if(!this.view){
67556             this.view = new Ext.grid.GridView(this.viewConfig);
67557         }
67558         return this.view;
67559     },
67560     /**
67561      * Called to get grid's drag proxy text, by default returns this.ddText.
67562      * @return {String} The text
67563      */
67564     getDragDropText : function(){
67565         var count = this.selModel.getCount();
67566         return String.format(this.ddText, count, count == 1 ? '' : 's');
67567     }
67568
67569     /**
67570      * @cfg {String/Number} activeItem
67571      * @hide
67572      */
67573     /**
67574      * @cfg {Boolean} autoDestroy
67575      * @hide
67576      */
67577     /**
67578      * @cfg {Object/String/Function} autoLoad
67579      * @hide
67580      */
67581     /**
67582      * @cfg {Boolean} autoWidth
67583      * @hide
67584      */
67585     /**
67586      * @cfg {Boolean/Number} bufferResize
67587      * @hide
67588      */
67589     /**
67590      * @cfg {String} defaultType
67591      * @hide
67592      */
67593     /**
67594      * @cfg {Object} defaults
67595      * @hide
67596      */
67597     /**
67598      * @cfg {Boolean} hideBorders
67599      * @hide
67600      */
67601     /**
67602      * @cfg {Mixed} items
67603      * @hide
67604      */
67605     /**
67606      * @cfg {String} layout
67607      * @hide
67608      */
67609     /**
67610      * @cfg {Object} layoutConfig
67611      * @hide
67612      */
67613     /**
67614      * @cfg {Boolean} monitorResize
67615      * @hide
67616      */
67617     /**
67618      * @property items
67619      * @hide
67620      */
67621     /**
67622      * @method add
67623      * @hide
67624      */
67625     /**
67626      * @method cascade
67627      * @hide
67628      */
67629     /**
67630      * @method doLayout
67631      * @hide
67632      */
67633     /**
67634      * @method find
67635      * @hide
67636      */
67637     /**
67638      * @method findBy
67639      * @hide
67640      */
67641     /**
67642      * @method findById
67643      * @hide
67644      */
67645     /**
67646      * @method findByType
67647      * @hide
67648      */
67649     /**
67650      * @method getComponent
67651      * @hide
67652      */
67653     /**
67654      * @method getLayout
67655      * @hide
67656      */
67657     /**
67658      * @method getUpdater
67659      * @hide
67660      */
67661     /**
67662      * @method insert
67663      * @hide
67664      */
67665     /**
67666      * @method load
67667      * @hide
67668      */
67669     /**
67670      * @method remove
67671      * @hide
67672      */
67673     /**
67674      * @event add
67675      * @hide
67676      */
67677     /**
67678      * @event afterlayout
67679      * @hide
67680      */
67681     /**
67682      * @event beforeadd
67683      * @hide
67684      */
67685     /**
67686      * @event beforeremove
67687      * @hide
67688      */
67689     /**
67690      * @event remove
67691      * @hide
67692      */
67693
67694
67695
67696     /**
67697      * @cfg {String} allowDomMove  @hide
67698      */
67699     /**
67700      * @cfg {String} autoEl @hide
67701      */
67702     /**
67703      * @cfg {String} applyTo  @hide
67704      */
67705     /**
67706      * @cfg {String} autoScroll  @hide
67707      */
67708     /**
67709      * @cfg {String} bodyBorder  @hide
67710      */
67711     /**
67712      * @cfg {String} bodyStyle  @hide
67713      */
67714     /**
67715      * @cfg {String} contentEl  @hide
67716      */
67717     /**
67718      * @cfg {String} disabledClass  @hide
67719      */
67720     /**
67721      * @cfg {String} elements  @hide
67722      */
67723     /**
67724      * @cfg {String} html  @hide
67725      */
67726     /**
67727      * @cfg {Boolean} preventBodyReset
67728      * @hide
67729      */
67730     /**
67731      * @property disabled
67732      * @hide
67733      */
67734     /**
67735      * @method applyToMarkup
67736      * @hide
67737      */
67738     /**
67739      * @method enable
67740      * @hide
67741      */
67742     /**
67743      * @method disable
67744      * @hide
67745      */
67746     /**
67747      * @method setDisabled
67748      * @hide
67749      */
67750 });
67751 Ext.reg('grid', Ext.grid.GridPanel);/**
67752  * @class Ext.grid.GridView
67753  * @extends Ext.util.Observable
67754  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
67755  * Methods of this class may be used to access user interface elements to enable
67756  * special display effects. Do not change the DOM structure of the user interface.</p>
67757  * <p>This class does not provide ways to manipulate the underlying data. The data
67758  * model of a Grid is held in an {@link Ext.data.Store}.</p>
67759  * @constructor
67760  * @param {Object} config
67761  */
67762 Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
67763     /**
67764      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
67765      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
67766      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
67767      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
67768      * (e.g., 'my-class another-class'). Example usage:
67769     <pre><code>
67770 viewConfig: {
67771     forceFit: true,
67772     showPreview: true, // custom property
67773     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
67774     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
67775         if(this.showPreview){
67776             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
67777             return 'x-grid3-row-expanded';
67778         }
67779         return 'x-grid3-row-collapsed';
67780     }
67781 },
67782     </code></pre>
67783      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
67784      * @param {Number} index The row index.
67785      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
67786      * customization of various aspects of a grid row.
67787      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
67788      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
67789      * <ul>
67790      * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
67791      * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
67792      * </ul>
67793      * The following property will be passed in, and may be appended to:
67794      * <ul>
67795      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
67796      * both the standard grid row, and any expansion row.</div></li>
67797      * </ul>
67798      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
67799      * @method getRowClass
67800      * @return {String} a CSS class name to add to the row.
67801      */
67802
67803     /**
67804      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
67805      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
67806      */
67807
67808     /**
67809      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
67810      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
67811     <pre><code>
67812     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
67813     </code></pre>
67814      */
67815
67816     /**
67817      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>).
67818      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
67819      * config to disable the <i>menu</i> for individual columns.  While this config is true the
67820      * following will be disabled:<div class="mdetail-params"><ul>
67821      * <li>clicking on header to sort</li>
67822      * <li>the trigger to reveal the menu.</li>
67823      * </ul></div>
67824      */
67825
67826     /**
67827      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
67828      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
67829      * See {@link Ext.grid.GridDragZone} for details.</p>
67830      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
67831      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
67832      * <li><i>after</i> the owning GridPanel has been rendered.</li>
67833      * </ul></div>
67834      * @property dragZone
67835      * @type {Ext.grid.GridDragZone}
67836      */
67837
67838     /**
67839      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
67840      * first load (defaults to <tt>true</tt>).
67841      */
67842     deferEmptyText : true,
67843
67844     /**
67845      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
67846      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
67847      * calculated.
67848      */
67849     scrollOffset : undefined,
67850
67851     /**
67852      * @cfg {Boolean} autoFill
67853      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
67854      * when the grid is <b>initially rendered</b>.  The
67855      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
67856      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
67857      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
67858      * See <tt>{@link #forceFit}</tt> also.
67859      */
67860     autoFill : false,
67861
67862     /**
67863      * @cfg {Boolean} forceFit
67864      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
67865      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
67866      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
67867      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
67868      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
67869      */
67870     forceFit : false,
67871
67872     /**
67873      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
67874      */
67875     sortClasses : ['sort-asc', 'sort-desc'],
67876
67877     /**
67878      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
67879      */
67880     sortAscText : 'Sort Ascending',
67881
67882     /**
67883      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
67884      */
67885     sortDescText : 'Sort Descending',
67886
67887     /**
67888      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
67889      */
67890     columnsText : 'Columns',
67891
67892     /**
67893      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
67894      * example overriding the default styling:
67895     <pre><code>
67896     .x-grid3-row-selected {background-color: yellow;}
67897     </code></pre>
67898      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
67899      * facets (like text) use something like:
67900     <pre><code>
67901     .x-grid3-row-selected .x-grid3-cell-inner {
67902         color: #FFCC00;
67903     }
67904     </code></pre>
67905      * @type String
67906      */
67907     selectedRowClass : 'x-grid3-row-selected',
67908
67909     // private
67910     borderWidth : 2,
67911     tdClass : 'x-grid3-cell',
67912     hdCls : 'x-grid3-hd',
67913     markDirty : true,
67914
67915     /**
67916      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
67917      */
67918     cellSelectorDepth : 4,
67919     /**
67920      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
67921      */
67922     rowSelectorDepth : 10,
67923
67924     /**
67925      * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
67926      */
67927     rowBodySelectorDepth : 10,
67928
67929     /**
67930      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
67931      */
67932     cellSelector : 'td.x-grid3-cell',
67933     /**
67934      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
67935      */
67936     rowSelector : 'div.x-grid3-row',
67937
67938     /**
67939      * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
67940      */
67941     rowBodySelector : 'div.x-grid3-row-body',
67942
67943     // private
67944     firstRowCls: 'x-grid3-row-first',
67945     lastRowCls: 'x-grid3-row-last',
67946     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
67947
67948     constructor : function(config){
67949         Ext.apply(this, config);
67950         // These events are only used internally by the grid components
67951         this.addEvents(
67952             /**
67953              * @event beforerowremoved
67954              * Internal UI Event. Fired before a row is removed.
67955              * @param {Ext.grid.GridView} view
67956              * @param {Number} rowIndex The index of the row to be removed.
67957              * @param {Ext.data.Record} record The Record to be removed
67958              */
67959             'beforerowremoved',
67960             /**
67961              * @event beforerowsinserted
67962              * Internal UI Event. Fired before rows are inserted.
67963              * @param {Ext.grid.GridView} view
67964              * @param {Number} firstRow The index of the first row to be inserted.
67965              * @param {Number} lastRow The index of the last row to be inserted.
67966              */
67967             'beforerowsinserted',
67968             /**
67969              * @event beforerefresh
67970              * Internal UI Event. Fired before the view is refreshed.
67971              * @param {Ext.grid.GridView} view
67972              */
67973             'beforerefresh',
67974             /**
67975              * @event rowremoved
67976              * Internal UI Event. Fired after a row is removed.
67977              * @param {Ext.grid.GridView} view
67978              * @param {Number} rowIndex The index of the row that was removed.
67979              * @param {Ext.data.Record} record The Record that was removed
67980              */
67981             'rowremoved',
67982             /**
67983              * @event rowsinserted
67984              * Internal UI Event. Fired after rows are inserted.
67985              * @param {Ext.grid.GridView} view
67986              * @param {Number} firstRow The index of the first inserted.
67987              * @param {Number} lastRow The index of the last row inserted.
67988              */
67989             'rowsinserted',
67990             /**
67991              * @event rowupdated
67992              * Internal UI Event. Fired after a row has been updated.
67993              * @param {Ext.grid.GridView} view
67994              * @param {Number} firstRow The index of the row updated.
67995              * @param {Ext.data.record} record The Record backing the row updated.
67996              */
67997             'rowupdated',
67998             /**
67999              * @event refresh
68000              * Internal UI Event. Fired after the GridView's body has been refreshed.
68001              * @param {Ext.grid.GridView} view
68002              */
68003             'refresh'
68004         );
68005         Ext.grid.GridView.superclass.constructor.call(this);
68006     },
68007
68008     /* -------------------------------- UI Specific ----------------------------- */
68009
68010     // private
68011     initTemplates : function(){
68012         var ts = this.templates || {};
68013         if(!ts.master){
68014             ts.master = new Ext.Template(
68015                 '<div class="x-grid3" hidefocus="true">',
68016                     '<div class="x-grid3-viewport">',
68017                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
68018                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
68019                     '</div>',
68020                     '<div class="x-grid3-resize-marker">&#160;</div>',
68021                     '<div class="x-grid3-resize-proxy">&#160;</div>',
68022                 '</div>'
68023             );
68024         }
68025
68026         if(!ts.header){
68027             ts.header = new Ext.Template(
68028                 '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
68029                 '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
68030                 '</table>'
68031             );
68032         }
68033
68034         if(!ts.hcell){
68035             ts.hcell = new Ext.Template(
68036                 '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
68037                 '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
68038                 '</div></td>'
68039             );
68040         }
68041
68042         if(!ts.body){
68043             ts.body = new Ext.Template('{rows}');
68044         }
68045
68046         if(!ts.row){
68047             ts.row = new Ext.Template(
68048                 '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
68049                 '<tbody><tr>{cells}</tr>',
68050                 (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
68051                 '</tbody></table></div>'
68052             );
68053         }
68054
68055         if(!ts.cell){
68056             ts.cell = new Ext.Template(
68057                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
68058                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
68059                     '</td>'
68060                     );
68061         }
68062
68063         for(var k in ts){
68064             var t = ts[k];
68065             if(t && Ext.isFunction(t.compile) && !t.compiled){
68066                 t.disableFormats = true;
68067                 t.compile();
68068             }
68069         }
68070
68071         this.templates = ts;
68072         this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
68073     },
68074
68075     // private
68076     fly : function(el){
68077         if(!this._flyweight){
68078             this._flyweight = new Ext.Element.Flyweight(document.body);
68079         }
68080         this._flyweight.dom = el;
68081         return this._flyweight;
68082     },
68083
68084     // private
68085     getEditorParent : function(){
68086         return this.scroller.dom;
68087     },
68088
68089     // private
68090     initElements : function(){
68091         var E = Ext.Element;
68092
68093         var el = this.grid.getGridEl().dom.firstChild;
68094         var cs = el.childNodes;
68095
68096         this.el = new E(el);
68097
68098         this.mainWrap = new E(cs[0]);
68099         this.mainHd = new E(this.mainWrap.dom.firstChild);
68100
68101         if(this.grid.hideHeaders){
68102             this.mainHd.setDisplayed(false);
68103         }
68104
68105         this.innerHd = this.mainHd.dom.firstChild;
68106         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
68107         if(this.forceFit){
68108             this.scroller.setStyle('overflow-x', 'hidden');
68109         }
68110         /**
68111          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
68112          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
68113          * @type Ext.Element
68114          * @property mainBody
68115          */
68116         this.mainBody = new E(this.scroller.dom.firstChild);
68117
68118         this.focusEl = new E(this.scroller.dom.childNodes[1]);
68119         this.focusEl.swallowEvent('click', true);
68120
68121         this.resizeMarker = new E(cs[1]);
68122         this.resizeProxy = new E(cs[2]);
68123     },
68124
68125     // private
68126     getRows : function(){
68127         return this.hasRows() ? this.mainBody.dom.childNodes : [];
68128     },
68129
68130     // finder methods, used with delegation
68131
68132     // private
68133     findCell : function(el){
68134         if(!el){
68135             return false;
68136         }
68137         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
68138     },
68139
68140     /**
68141      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
68142      * See also {@link #findRowIndex}
68143      * @param {HTMLElement} el The target element
68144      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
68145      */
68146     findCellIndex : function(el, requiredCls){
68147         var cell = this.findCell(el);
68148         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
68149             return this.getCellIndex(cell);
68150         }
68151         return false;
68152     },
68153
68154     // private
68155     getCellIndex : function(el){
68156         if(el){
68157             var m = el.className.match(this.colRe);
68158             if(m && m[1]){
68159                 return this.cm.getIndexById(m[1]);
68160             }
68161         }
68162         return false;
68163     },
68164
68165     // private
68166     findHeaderCell : function(el){
68167         var cell = this.findCell(el);
68168         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
68169     },
68170
68171     // private
68172     findHeaderIndex : function(el){
68173         return this.findCellIndex(el, this.hdCls);
68174     },
68175
68176     /**
68177      * Return the HtmlElement representing the grid row which contains the passed element.
68178      * @param {HTMLElement} el The target HTMLElement
68179      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
68180      */
68181     findRow : function(el){
68182         if(!el){
68183             return false;
68184         }
68185         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
68186     },
68187
68188     /**
68189      * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
68190      * See also {@link #findCellIndex}
68191      * @param {HTMLElement} el The target HTMLElement
68192      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
68193      */
68194     findRowIndex : function(el){
68195         var r = this.findRow(el);
68196         return r ? r.rowIndex : false;
68197     },
68198
68199     /**
68200      * Return the HtmlElement representing the grid row body which contains the passed element.
68201      * @param {HTMLElement} el The target HTMLElement
68202      * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
68203      */
68204     findRowBody : function(el){
68205         if(!el){
68206             return false;
68207         }
68208         return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
68209     },
68210
68211     // getter methods for fetching elements dynamically in the grid
68212
68213     /**
68214      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
68215      * @param {Number} index The row index
68216      * @return {HtmlElement} The div element.
68217      */
68218     getRow : function(row){
68219         return this.getRows()[row];
68220     },
68221
68222     /**
68223      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
68224      * @param {Number} row The row index in which to find the cell.
68225      * @param {Number} col The column index of the cell.
68226      * @return {HtmlElement} The td at the specified coordinates.
68227      */
68228     getCell : function(row, col){
68229         return this.getRow(row).getElementsByTagName('td')[col];
68230     },
68231
68232     /**
68233      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
68234      * @param {Number} index The column index
68235      * @return {HtmlElement} The td element.
68236      */
68237     getHeaderCell : function(index){
68238         return this.mainHd.dom.getElementsByTagName('td')[index];
68239     },
68240
68241     // manipulating elements
68242
68243     // private - use getRowClass to apply custom row classes
68244     addRowClass : function(row, cls){
68245         var r = this.getRow(row);
68246         if(r){
68247             this.fly(r).addClass(cls);
68248         }
68249     },
68250
68251     // private
68252     removeRowClass : function(row, cls){
68253         var r = this.getRow(row);
68254         if(r){
68255             this.fly(r).removeClass(cls);
68256         }
68257     },
68258
68259     // private
68260     removeRow : function(row){
68261         Ext.removeNode(this.getRow(row));
68262         this.syncFocusEl(row);
68263     },
68264
68265     // private
68266     removeRows : function(firstRow, lastRow){
68267         var bd = this.mainBody.dom;
68268         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
68269             Ext.removeNode(bd.childNodes[firstRow]);
68270         }
68271         this.syncFocusEl(firstRow);
68272     },
68273
68274     // scrolling stuff
68275
68276     // private
68277     getScrollState : function(){
68278         var sb = this.scroller.dom;
68279         return {left: sb.scrollLeft, top: sb.scrollTop};
68280     },
68281
68282     // private
68283     restoreScroll : function(state){
68284         var sb = this.scroller.dom;
68285         sb.scrollLeft = state.left;
68286         sb.scrollTop = state.top;
68287     },
68288
68289     /**
68290      * Scrolls the grid to the top
68291      */
68292     scrollToTop : function(){
68293         this.scroller.dom.scrollTop = 0;
68294         this.scroller.dom.scrollLeft = 0;
68295     },
68296
68297     // private
68298     syncScroll : function(){
68299         this.syncHeaderScroll();
68300         var mb = this.scroller.dom;
68301         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
68302     },
68303
68304     // private
68305     syncHeaderScroll : function(){
68306         var mb = this.scroller.dom;
68307         this.innerHd.scrollLeft = mb.scrollLeft;
68308         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
68309     },
68310
68311     // private
68312     updateSortIcon : function(col, dir){
68313         var sc = this.sortClasses;
68314         var hds = this.mainHd.select('td').removeClass(sc);
68315         hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
68316     },
68317
68318     // private
68319     updateAllColumnWidths : function(){
68320         var tw   = this.getTotalWidth(),
68321             clen = this.cm.getColumnCount(),
68322             ws   = [],
68323             len,
68324             i;
68325
68326         for(i = 0; i < clen; i++){
68327             ws[i] = this.getColumnWidth(i);
68328         }
68329
68330         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68331         this.innerHd.firstChild.firstChild.style.width = tw;
68332         this.mainBody.dom.style.width = tw;
68333
68334         for(i = 0; i < clen; i++){
68335             var hd = this.getHeaderCell(i);
68336             hd.style.width = ws[i];
68337         }
68338
68339         var ns = this.getRows(), row, trow;
68340         for(i = 0, len = ns.length; i < len; i++){
68341             row = ns[i];
68342             row.style.width = tw;
68343             if(row.firstChild){
68344                 row.firstChild.style.width = tw;
68345                 trow = row.firstChild.rows[0];
68346                 for (var j = 0; j < clen; j++) {
68347                    trow.childNodes[j].style.width = ws[j];
68348                 }
68349             }
68350         }
68351
68352         this.onAllColumnWidthsUpdated(ws, tw);
68353     },
68354
68355     // private
68356     updateColumnWidth : function(col, width){
68357         var w = this.getColumnWidth(col);
68358         var tw = this.getTotalWidth();
68359         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68360         this.innerHd.firstChild.firstChild.style.width = tw;
68361         this.mainBody.dom.style.width = tw;
68362         var hd = this.getHeaderCell(col);
68363         hd.style.width = w;
68364
68365         var ns = this.getRows(), row;
68366         for(var i = 0, len = ns.length; i < len; i++){
68367             row = ns[i];
68368             row.style.width = tw;
68369             if(row.firstChild){
68370                 row.firstChild.style.width = tw;
68371                 row.firstChild.rows[0].childNodes[col].style.width = w;
68372             }
68373         }
68374
68375         this.onColumnWidthUpdated(col, w, tw);
68376     },
68377
68378     // private
68379     updateColumnHidden : function(col, hidden){
68380         var tw = this.getTotalWidth();
68381         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68382         this.innerHd.firstChild.firstChild.style.width = tw;
68383         this.mainBody.dom.style.width = tw;
68384         var display = hidden ? 'none' : '';
68385
68386         var hd = this.getHeaderCell(col);
68387         hd.style.display = display;
68388
68389         var ns = this.getRows(), row;
68390         for(var i = 0, len = ns.length; i < len; i++){
68391             row = ns[i];
68392             row.style.width = tw;
68393             if(row.firstChild){
68394                 row.firstChild.style.width = tw;
68395                 row.firstChild.rows[0].childNodes[col].style.display = display;
68396             }
68397         }
68398
68399         this.onColumnHiddenUpdated(col, hidden, tw);
68400         delete this.lastViewWidth; // force recalc
68401         this.layout();
68402     },
68403
68404     /**
68405      * @private
68406      * Renders all of the rows to a string buffer and returns the string. This is called internally
68407      * by renderRows and performs the actual string building for the rows - it does not inject HTML into the DOM.
68408      * @param {Array} columns The column data acquired from getColumnData.
68409      * @param {Array} records The array of records to render
68410      * @param {Ext.data.Store} store The store to render the rows from
68411      * @param {Number} startRow The index of the first row being rendered. Sometimes we only render a subset of
68412      * the rows so this is used to maintain logic for striping etc
68413      * @param {Number} colCount The total number of columns in the column model
68414      * @param {Boolean} stripe True to stripe the rows
68415      * @return {String} A string containing the HTML for the rendered rows
68416      */
68417     doRender : function(columns, records, store, startRow, colCount, stripe) {
68418         var templates    = this.templates,
68419             cellTemplate = templates.cell,
68420             rowTemplate  = templates.row,
68421             last         = colCount - 1;
68422
68423         var tstyle = 'width:' + this.getTotalWidth() + ';';
68424
68425         // buffers
68426         var rowBuffer = [],
68427             colBuffer = [],
68428             rowParams = {tstyle: tstyle},
68429             meta      = {},
68430             column,
68431             record;
68432
68433         //build up each row's HTML
68434         for (var j = 0, len = records.length; j < len; j++) {
68435             record    = records[j];
68436             colBuffer = [];
68437
68438             var rowIndex = j + startRow;
68439
68440             //build up each column's HTML
68441             for (var i = 0; i < colCount; i++) {
68442                 column = columns[i];
68443
68444                 meta.id    = column.id;
68445                 meta.css   = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
68446                 meta.attr  = meta.cellAttr = '';
68447                 meta.style = column.style;
68448                 meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
68449
68450                 if (Ext.isEmpty(meta.value)) {
68451                     meta.value = '&#160;';
68452                 }
68453
68454                 if (this.markDirty && record.dirty && Ext.isDefined(record.modified[column.name])) {
68455                     meta.css += ' x-grid3-dirty-cell';
68456                 }
68457
68458                 colBuffer[colBuffer.length] = cellTemplate.apply(meta);
68459             }
68460
68461             //set up row striping and row dirtiness CSS classes
68462             var alt = [];
68463
68464             if (stripe && ((rowIndex + 1) % 2 === 0)) {
68465                 alt[0] = 'x-grid3-row-alt';
68466             }
68467
68468             if (record.dirty) {
68469                 alt[1] = ' x-grid3-dirty-row';
68470             }
68471
68472             rowParams.cols = colCount;
68473
68474             if (this.getRowClass) {
68475                 alt[2] = this.getRowClass(record, rowIndex, rowParams, store);
68476             }
68477
68478             rowParams.alt   = alt.join(' ');
68479             rowParams.cells = colBuffer.join('');
68480
68481             rowBuffer[rowBuffer.length] = rowTemplate.apply(rowParams);
68482         }
68483
68484         return rowBuffer.join('');
68485     },
68486
68487     // private
68488     processRows : function(startRow, skipStripe) {
68489         if (!this.ds || this.ds.getCount() < 1) {
68490             return;
68491         }
68492
68493         var rows = this.getRows(),
68494             len  = rows.length,
68495             i, r;
68496
68497         skipStripe = skipStripe || !this.grid.stripeRows;
68498         startRow   = startRow   || 0;
68499
68500         for (i = 0; i<len; i++) {
68501             r = rows[i];
68502             if (r) {
68503                 r.rowIndex = i;
68504                 if (!skipStripe) {
68505                     r.className = r.className.replace(this.rowClsRe, ' ');
68506                     if ((i + 1) % 2 === 0){
68507                         r.className += ' x-grid3-row-alt';
68508                     }
68509                 }
68510             }
68511         }
68512
68513         // add first/last-row classes
68514         if (startRow === 0) {
68515             Ext.fly(rows[0]).addClass(this.firstRowCls);
68516         }
68517
68518         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
68519     },
68520
68521     afterRender : function(){
68522         if(!this.ds || !this.cm){
68523             return;
68524         }
68525         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
68526         this.processRows(0, true);
68527
68528         if(this.deferEmptyText !== true){
68529             this.applyEmptyText();
68530         }
68531         this.grid.fireEvent('viewready', this.grid);
68532     },
68533
68534     /**
68535      * @private
68536      * Renders each of the UI elements in turn. This is called internally, once, by this.render. It does not
68537      * render rows from the store, just the surrounding UI elements. It also sets up listeners on the UI elements
68538      * and sets up options like column menus, moving and resizing.
68539      */
68540     renderUI : function() {
68541         var templates = this.templates,
68542             header    = this.renderHeaders(),
68543             body      = templates.body.apply({rows:'&#160;'});
68544
68545         var html = templates.master.apply({
68546             body  : body,
68547             header: header,
68548             ostyle: 'width:' + this.getOffsetWidth() + ';',
68549             bstyle: 'width:' + this.getTotalWidth()  + ';'
68550         });
68551
68552         var g = this.grid;
68553
68554         g.getGridEl().dom.innerHTML = html;
68555
68556         this.initElements();
68557
68558         // get mousedowns early
68559         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
68560
68561         this.mainHd.on({
68562             scope    : this,
68563             mouseover: this.handleHdOver,
68564             mouseout : this.handleHdOut,
68565             mousemove: this.handleHdMove
68566         });
68567
68568         this.scroller.on('scroll', this.syncScroll,  this);
68569         if (g.enableColumnResize !== false) {
68570             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
68571         }
68572
68573         if (g.enableColumnMove) {
68574             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
68575             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
68576         }
68577
68578         if (g.enableHdMenu !== false) {
68579             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
68580             this.hmenu.add(
68581                 {itemId:'asc',  text: this.sortAscText,  cls: 'xg-hmenu-sort-asc'},
68582                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
68583             );
68584
68585             if (g.enableColumnHide !== false) {
68586                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
68587                 this.colMenu.on({
68588                     scope     : this,
68589                     beforeshow: this.beforeColMenuShow,
68590                     itemclick : this.handleHdMenuClick
68591                 });
68592                 this.hmenu.add('-', {
68593                     itemId:'columns',
68594                     hideOnClick: false,
68595                     text: this.columnsText,
68596                     menu: this.colMenu,
68597                     iconCls: 'x-cols-icon'
68598                 });
68599             }
68600
68601             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
68602         }
68603
68604         if (g.trackMouseOver) {
68605             this.mainBody.on({
68606                 scope    : this,
68607                 mouseover: this.onRowOver,
68608                 mouseout : this.onRowOut
68609             });
68610         }
68611
68612         if (g.enableDragDrop || g.enableDrag) {
68613             this.dragZone = new Ext.grid.GridDragZone(g, {
68614                 ddGroup : g.ddGroup || 'GridDD'
68615             });
68616         }
68617
68618         this.updateHeaderSortState();
68619     },
68620
68621     // private
68622     processEvent : function(name, e) {
68623         var t = e.getTarget(),
68624             g = this.grid,
68625             header = this.findHeaderIndex(t);
68626         g.fireEvent(name, e);
68627         if (header !== false) {
68628             g.fireEvent('header' + name, g, header, e);
68629         } else {
68630             var row = this.findRowIndex(t),
68631                 cell,
68632                 body;
68633             if (row !== false) {
68634                 g.fireEvent('row' + name, g, row, e);
68635                 cell = this.findCellIndex(t);
68636                 if (cell !== false) {
68637                     g.fireEvent('cell' + name, g, row, cell, e);
68638                 } else {
68639                     body = this.findRowBody(t);
68640                     if (body) {
68641                         g.fireEvent('rowbody' + name, g, row, e);
68642                     }
68643                 }
68644             } else {
68645                 g.fireEvent('container' + name, g, e);
68646             }
68647         }
68648     },
68649
68650     // private
68651     layout : function() {
68652         if(!this.mainBody){
68653             return; // not rendered
68654         }
68655         var g = this.grid;
68656         var c = g.getGridEl();
68657         var csize = c.getSize(true);
68658         var vw = csize.width;
68659
68660         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
68661             return;
68662         }
68663
68664         if(g.autoHeight){
68665             this.scroller.dom.style.overflow = 'visible';
68666             if(Ext.isWebKit){
68667                 this.scroller.dom.style.position = 'static';
68668             }
68669         }else{
68670             this.el.setSize(csize.width, csize.height);
68671
68672             var hdHeight = this.mainHd.getHeight();
68673             var vh = csize.height - (hdHeight);
68674
68675             this.scroller.setSize(vw, vh);
68676             if(this.innerHd){
68677                 this.innerHd.style.width = (vw)+'px';
68678             }
68679         }
68680         if(this.forceFit){
68681             if(this.lastViewWidth != vw){
68682                 this.fitColumns(false, false);
68683                 this.lastViewWidth = vw;
68684             }
68685         }else {
68686             this.autoExpand();
68687             this.syncHeaderScroll();
68688         }
68689         this.onLayout(vw, vh);
68690     },
68691
68692     // template functions for subclasses and plugins
68693     // these functions include precalculated values
68694     onLayout : function(vw, vh){
68695         // do nothing
68696     },
68697
68698     onColumnWidthUpdated : function(col, w, tw){
68699         //template method
68700     },
68701
68702     onAllColumnWidthsUpdated : function(ws, tw){
68703         //template method
68704     },
68705
68706     onColumnHiddenUpdated : function(col, hidden, tw){
68707         // template method
68708     },
68709
68710     updateColumnText : function(col, text){
68711         // template method
68712     },
68713
68714     afterMove : function(colIndex){
68715         // template method
68716     },
68717
68718     /* ----------------------------------- Core Specific -------------------------------------------*/
68719     // private
68720     init : function(grid){
68721         this.grid = grid;
68722
68723         this.initTemplates();
68724         this.initData(grid.store, grid.colModel);
68725         this.initUI(grid);
68726     },
68727
68728     // private
68729     getColumnId : function(index){
68730       return this.cm.getColumnId(index);
68731     },
68732
68733     // private
68734     getOffsetWidth : function() {
68735         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
68736     },
68737
68738     getScrollOffset: function(){
68739         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
68740     },
68741
68742     /**
68743      * @private
68744      * Renders the header row using the 'header' template. Does not inject the HTML into the DOM, just
68745      * returns a string.
68746      * @return {String} Rendered header row
68747      */
68748     renderHeaders : function() {
68749         var cm   = this.cm,
68750             ts   = this.templates,
68751             ct   = ts.hcell,
68752             cb   = [],
68753             p    = {},
68754             len  = cm.getColumnCount(),
68755             last = len - 1;
68756
68757         for (var i = 0; i < len; i++) {
68758             p.id = cm.getColumnId(i);
68759             p.value = cm.getColumnHeader(i) || '';
68760             p.style = this.getColumnStyle(i, true);
68761             p.tooltip = this.getColumnTooltip(i);
68762             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
68763
68764             if (cm.config[i].align == 'right') {
68765                 p.istyle = 'padding-right:16px';
68766             } else {
68767                 delete p.istyle;
68768             }
68769             cb[cb.length] = ct.apply(p);
68770         }
68771         return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
68772     },
68773
68774     // private
68775     getColumnTooltip : function(i){
68776         var tt = this.cm.getColumnTooltip(i);
68777         if(tt){
68778             if(Ext.QuickTips.isEnabled()){
68779                 return 'ext:qtip="'+tt+'"';
68780             }else{
68781                 return 'title="'+tt+'"';
68782             }
68783         }
68784         return '';
68785     },
68786
68787     // private
68788     beforeUpdate : function(){
68789         this.grid.stopEditing(true);
68790     },
68791
68792     // private
68793     updateHeaders : function(){
68794         this.innerHd.firstChild.innerHTML = this.renderHeaders();
68795         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68796         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
68797     },
68798
68799     /**
68800      * Focuses the specified row.
68801      * @param {Number} row The row index
68802      */
68803     focusRow : function(row){
68804         this.focusCell(row, 0, false);
68805     },
68806
68807     /**
68808      * Focuses the specified cell.
68809      * @param {Number} row The row index
68810      * @param {Number} col The column index
68811      */
68812     focusCell : function(row, col, hscroll){
68813         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
68814         if(Ext.isGecko){
68815             this.focusEl.focus();
68816         }else{
68817             this.focusEl.focus.defer(1, this.focusEl);
68818         }
68819     },
68820
68821     resolveCell : function(row, col, hscroll){
68822         if(!Ext.isNumber(row)){
68823             row = row.rowIndex;
68824         }
68825         if(!this.ds){
68826             return null;
68827         }
68828         if(row < 0 || row >= this.ds.getCount()){
68829             return null;
68830         }
68831         col = (col !== undefined ? col : 0);
68832
68833         var rowEl = this.getRow(row),
68834             cm = this.cm,
68835             colCount = cm.getColumnCount(),
68836             cellEl;
68837         if(!(hscroll === false && col === 0)){
68838             while(col < colCount && cm.isHidden(col)){
68839                 col++;
68840             }
68841             cellEl = this.getCell(row, col);
68842         }
68843
68844         return {row: rowEl, cell: cellEl};
68845     },
68846
68847     getResolvedXY : function(resolved){
68848         if(!resolved){
68849             return null;
68850         }
68851         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
68852         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
68853     },
68854
68855     syncFocusEl : function(row, col, hscroll){
68856         var xy = row;
68857         if(!Ext.isArray(xy)){
68858             row = Math.min(row, Math.max(0, this.getRows().length-1));
68859             if (isNaN(row)) {
68860                 return;
68861             }
68862             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
68863         }
68864         this.focusEl.setXY(xy||this.scroller.getXY());
68865     },
68866
68867     ensureVisible : function(row, col, hscroll){
68868         var resolved = this.resolveCell(row, col, hscroll);
68869         if(!resolved || !resolved.row){
68870             return;
68871         }
68872
68873         var rowEl = resolved.row,
68874             cellEl = resolved.cell,
68875             c = this.scroller.dom,
68876             ctop = 0,
68877             p = rowEl,
68878             stop = this.el.dom;
68879
68880         while(p && p != stop){
68881             ctop += p.offsetTop;
68882             p = p.offsetParent;
68883         }
68884
68885         ctop -= this.mainHd.dom.offsetHeight;
68886         stop = parseInt(c.scrollTop, 10);
68887
68888         var cbot = ctop + rowEl.offsetHeight,
68889             ch = c.clientHeight,
68890             sbot = stop + ch;
68891
68892
68893         if(ctop < stop){
68894           c.scrollTop = ctop;
68895         }else if(cbot > sbot){
68896             c.scrollTop = cbot-ch;
68897         }
68898
68899         if(hscroll !== false){
68900             var cleft = parseInt(cellEl.offsetLeft, 10);
68901             var cright = cleft + cellEl.offsetWidth;
68902
68903             var sleft = parseInt(c.scrollLeft, 10);
68904             var sright = sleft + c.clientWidth;
68905             if(cleft < sleft){
68906                 c.scrollLeft = cleft;
68907             }else if(cright > sright){
68908                 c.scrollLeft = cright-c.clientWidth;
68909             }
68910         }
68911         return this.getResolvedXY(resolved);
68912     },
68913
68914     // private
68915     insertRows : function(dm, firstRow, lastRow, isUpdate) {
68916         var last = dm.getCount() - 1;
68917         if( !isUpdate && firstRow === 0 && lastRow >= last) {
68918             this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
68919                 this.refresh();
68920             this.fireEvent('rowsinserted', this, firstRow, lastRow);
68921         } else {
68922             if (!isUpdate) {
68923                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
68924             }
68925             var html = this.renderRows(firstRow, lastRow),
68926                 before = this.getRow(firstRow);
68927             if (before) {
68928                 if(firstRow === 0){
68929                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
68930                 }
68931                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
68932             } else {
68933                 var r = this.getRow(last - 1);
68934                 if(r){
68935                     Ext.fly(r).removeClass(this.lastRowCls);
68936                 }
68937                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
68938             }
68939             if (!isUpdate) {
68940                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
68941                 this.processRows(firstRow);
68942             } else if (firstRow === 0 || firstRow >= last) {
68943                 //ensure first/last row is kept after an update.
68944                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
68945             }
68946         }
68947         this.syncFocusEl(firstRow);
68948     },
68949
68950     // private
68951     deleteRows : function(dm, firstRow, lastRow){
68952         if(dm.getRowCount()<1){
68953             this.refresh();
68954         }else{
68955             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
68956
68957             this.removeRows(firstRow, lastRow);
68958
68959             this.processRows(firstRow);
68960             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
68961         }
68962     },
68963
68964     // private
68965     getColumnStyle : function(col, isHeader){
68966         var style = !isHeader ? (this.cm.config[col].css || '') : '';
68967         style += 'width:'+this.getColumnWidth(col)+';';
68968         if(this.cm.isHidden(col)){
68969             style += 'display:none;';
68970         }
68971         var align = this.cm.config[col].align;
68972         if(align){
68973             style += 'text-align:'+align+';';
68974         }
68975         return style;
68976     },
68977
68978     // private
68979     getColumnWidth : function(col){
68980         var w = this.cm.getColumnWidth(col);
68981         if(Ext.isNumber(w)){
68982             return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
68983         }
68984         return w;
68985     },
68986
68987     // private
68988     getTotalWidth : function(){
68989         return this.cm.getTotalWidth()+'px';
68990     },
68991
68992     // private
68993     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
68994         var cm = this.cm, i;
68995         var tw = cm.getTotalWidth(false);
68996         var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
68997
68998         if(aw < 20){ // not initialized, so don't screw up the default widths
68999             return;
69000         }
69001         var extra = aw - tw;
69002
69003         if(extra === 0){
69004             return false;
69005         }
69006
69007         var vc = cm.getColumnCount(true);
69008         var ac = vc-(Ext.isNumber(omitColumn) ? 1 : 0);
69009         if(ac === 0){
69010             ac = 1;
69011             omitColumn = undefined;
69012         }
69013         var colCount = cm.getColumnCount();
69014         var cols = [];
69015         var extraCol = 0;
69016         var width = 0;
69017         var w;
69018         for (i = 0; i < colCount; i++){
69019             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
69020                 w = cm.getColumnWidth(i);
69021                 cols.push(i);
69022                 extraCol = i;
69023                 cols.push(w);
69024                 width += w;
69025             }
69026         }
69027         var frac = (aw - cm.getTotalWidth())/width;
69028         while (cols.length){
69029             w = cols.pop();
69030             i = cols.pop();
69031             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
69032         }
69033
69034         if((tw = cm.getTotalWidth(false)) > aw){
69035             var adjustCol = ac != vc ? omitColumn : extraCol;
69036              cm.setColumnWidth(adjustCol, Math.max(1,
69037                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
69038         }
69039
69040         if(preventRefresh !== true){
69041             this.updateAllColumnWidths();
69042         }
69043
69044
69045         return true;
69046     },
69047
69048     // private
69049     autoExpand : function(preventUpdate){
69050         var g = this.grid, cm = this.cm;
69051         if(!this.userResized && g.autoExpandColumn){
69052             var tw = cm.getTotalWidth(false);
69053             var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
69054             if(tw != aw){
69055                 var ci = cm.getIndexById(g.autoExpandColumn);
69056                 var currentWidth = cm.getColumnWidth(ci);
69057                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
69058                 if(cw != currentWidth){
69059                     cm.setColumnWidth(ci, cw, true);
69060                     if(preventUpdate !== true){
69061                         this.updateColumnWidth(ci, cw);
69062                     }
69063                 }
69064             }
69065         }
69066     },
69067
69068     /**
69069      * @private
69070      * Returns an array of column configurations - one for each column
69071      * @return {Array} Array of column config objects. This includes the column name, renderer, id style and renderer
69072      */
69073     getColumnData : function(){
69074         // build a map for all the columns
69075         var cs       = [],
69076             cm       = this.cm,
69077             colCount = cm.getColumnCount();
69078
69079         for (var i = 0; i < colCount; i++) {
69080             var name = cm.getDataIndex(i);
69081
69082             cs[i] = {
69083                 name    : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
69084                 renderer: cm.getRenderer(i),
69085                 scope   : cm.getRendererScope(i),
69086                 id      : cm.getColumnId(i),
69087                 style   : this.getColumnStyle(i)
69088             };
69089         }
69090
69091         return cs;
69092     },
69093
69094     // private
69095     renderRows : function(startRow, endRow){
69096         // pull in all the crap needed to render rows
69097         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
69098         var colCount = cm.getColumnCount();
69099
69100         if(ds.getCount() < 1){
69101             return '';
69102         }
69103
69104         var cs = this.getColumnData();
69105
69106         startRow = startRow || 0;
69107         endRow = !Ext.isDefined(endRow) ? ds.getCount()-1 : endRow;
69108
69109         // records to render
69110         var rs = ds.getRange(startRow, endRow);
69111
69112         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
69113     },
69114
69115     // private
69116     renderBody : function(){
69117         var markup = this.renderRows() || '&#160;';
69118         return this.templates.body.apply({rows: markup});
69119     },
69120
69121     // private
69122     refreshRow : function(record){
69123         var ds = this.ds, index;
69124         if(Ext.isNumber(record)){
69125             index = record;
69126             record = ds.getAt(index);
69127             if(!record){
69128                 return;
69129             }
69130         }else{
69131             index = ds.indexOf(record);
69132             if(index < 0){
69133                 return;
69134             }
69135         }
69136         this.insertRows(ds, index, index, true);
69137         this.getRow(index).rowIndex = index;
69138         this.onRemove(ds, record, index+1, true);
69139         this.fireEvent('rowupdated', this, index, record);
69140     },
69141
69142     /**
69143      * Refreshs the grid UI
69144      * @param {Boolean} headersToo (optional) True to also refresh the headers
69145      */
69146     refresh : function(headersToo){
69147         this.fireEvent('beforerefresh', this);
69148         this.grid.stopEditing(true);
69149
69150         var result = this.renderBody();
69151         this.mainBody.update(result).setWidth(this.getTotalWidth());
69152         if(headersToo === true){
69153             this.updateHeaders();
69154             this.updateHeaderSortState();
69155         }
69156         this.processRows(0, true);
69157         this.layout();
69158         this.applyEmptyText();
69159         this.fireEvent('refresh', this);
69160     },
69161
69162     /**
69163      * @private
69164      * Displays the configured emptyText if there are currently no rows to display
69165      */
69166     applyEmptyText : function(){
69167         if(this.emptyText && !this.hasRows()){
69168             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
69169         }
69170     },
69171
69172     /**
69173      * @private
69174      * Adds sorting classes to the column headers based on the bound store's sortInfo. Fires the 'sortchange' event
69175      * if the sorting has changed since this function was last run.
69176      */
69177     updateHeaderSortState : function(){
69178         var state = this.ds.getSortState();
69179         if (!state) {
69180             return;
69181         }
69182
69183         if (!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)) {
69184             this.grid.fireEvent('sortchange', this.grid, state);
69185         }
69186
69187         this.sortState = state;
69188
69189         var sortColumn = this.cm.findColumnIndex(state.field);
69190         if (sortColumn != -1){
69191             var sortDir = state.direction;
69192             this.updateSortIcon(sortColumn, sortDir);
69193         }
69194     },
69195
69196     /**
69197      * @private
69198      * Removes any sorting indicator classes from the column headers
69199      */
69200     clearHeaderSortState : function(){
69201         if (!this.sortState) {
69202             return;
69203         }
69204         this.grid.fireEvent('sortchange', this.grid, null);
69205         this.mainHd.select('td').removeClass(this.sortClasses);
69206         delete this.sortState;
69207     },
69208
69209     // private
69210     destroy : function(){
69211         if (this.scrollToTopTask && this.scrollToTopTask.cancel){
69212             this.scrollToTopTask.cancel();
69213         }
69214         if(this.colMenu){
69215             Ext.menu.MenuMgr.unregister(this.colMenu);
69216             this.colMenu.destroy();
69217             delete this.colMenu;
69218         }
69219         if(this.hmenu){
69220             Ext.menu.MenuMgr.unregister(this.hmenu);
69221             this.hmenu.destroy();
69222             delete this.hmenu;
69223         }
69224
69225         this.initData(null, null);
69226         this.purgeListeners();
69227         Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
69228
69229         if(this.grid.enableColumnMove){
69230             Ext.destroy(
69231                 this.columnDrag.el,
69232                 this.columnDrag.proxy.ghost,
69233                 this.columnDrag.proxy.el,
69234                 this.columnDrop.el,
69235                 this.columnDrop.proxyTop,
69236                 this.columnDrop.proxyBottom,
69237                 this.columnDrag.dragData.ddel,
69238                 this.columnDrag.dragData.header
69239             );
69240             if (this.columnDrag.proxy.anim) {
69241                 Ext.destroy(this.columnDrag.proxy.anim);
69242             }
69243             delete this.columnDrag.proxy.ghost;
69244             delete this.columnDrag.dragData.ddel;
69245             delete this.columnDrag.dragData.header;
69246             this.columnDrag.destroy();
69247             delete Ext.dd.DDM.locationCache[this.columnDrag.id];
69248             delete this.columnDrag._domRef;
69249
69250             delete this.columnDrop.proxyTop;
69251             delete this.columnDrop.proxyBottom;
69252             this.columnDrop.destroy();
69253             delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
69254             delete this.columnDrop._domRef;
69255             delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
69256         }
69257
69258         if (this.splitZone){ // enableColumnResize
69259             this.splitZone.destroy();
69260             delete this.splitZone._domRef;
69261             delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
69262         }
69263
69264         Ext.fly(this.innerHd).removeAllListeners();
69265         Ext.removeNode(this.innerHd);
69266         delete this.innerHd;
69267
69268         Ext.destroy(
69269             this.el,
69270             this.mainWrap,
69271             this.mainHd,
69272             this.scroller,
69273             this.mainBody,
69274             this.focusEl,
69275             this.resizeMarker,
69276             this.resizeProxy,
69277             this.activeHdBtn,
69278             this.dragZone,
69279             this.splitZone,
69280             this._flyweight
69281         );
69282
69283         delete this.grid.container;
69284
69285         if(this.dragZone){
69286             this.dragZone.destroy();
69287         }
69288
69289         Ext.dd.DDM.currentTarget = null;
69290         delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
69291
69292         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
69293     },
69294
69295     // private
69296     onDenyColumnHide : function(){
69297
69298     },
69299
69300     // private
69301     render : function(){
69302         if(this.autoFill){
69303             var ct = this.grid.ownerCt;
69304             if (ct && ct.getLayout()){
69305                 ct.on('afterlayout', function(){
69306                     this.fitColumns(true, true);
69307                     this.updateHeaders();
69308                 }, this, {single: true});
69309             }else{
69310                 this.fitColumns(true, true);
69311             }
69312         }else if(this.forceFit){
69313             this.fitColumns(true, false);
69314         }else if(this.grid.autoExpandColumn){
69315             this.autoExpand(true);
69316         }
69317
69318         this.renderUI();
69319     },
69320
69321     /* --------------------------------- Model Events and Handlers --------------------------------*/
69322     // private
69323     initData : function(ds, cm){
69324         if(this.ds){
69325             this.ds.un('load', this.onLoad, this);
69326             this.ds.un('datachanged', this.onDataChange, this);
69327             this.ds.un('add', this.onAdd, this);
69328             this.ds.un('remove', this.onRemove, this);
69329             this.ds.un('update', this.onUpdate, this);
69330             this.ds.un('clear', this.onClear, this);
69331             if(this.ds !== ds && this.ds.autoDestroy){
69332                 this.ds.destroy();
69333             }
69334         }
69335         if(ds){
69336             ds.on({
69337                 scope: this,
69338                 load: this.onLoad,
69339                 datachanged: this.onDataChange,
69340                 add: this.onAdd,
69341                 remove: this.onRemove,
69342                 update: this.onUpdate,
69343                 clear: this.onClear
69344             });
69345         }
69346         this.ds = ds;
69347
69348         if(this.cm){
69349             this.cm.un('configchange', this.onColConfigChange, this);
69350             this.cm.un('widthchange', this.onColWidthChange, this);
69351             this.cm.un('headerchange', this.onHeaderChange, this);
69352             this.cm.un('hiddenchange', this.onHiddenChange, this);
69353             this.cm.un('columnmoved', this.onColumnMove, this);
69354         }
69355         if(cm){
69356             delete this.lastViewWidth;
69357             cm.on({
69358                 scope: this,
69359                 configchange: this.onColConfigChange,
69360                 widthchange: this.onColWidthChange,
69361                 headerchange: this.onHeaderChange,
69362                 hiddenchange: this.onHiddenChange,
69363                 columnmoved: this.onColumnMove
69364             });
69365         }
69366         this.cm = cm;
69367     },
69368
69369     // private
69370     onDataChange : function(){
69371         this.refresh();
69372         this.updateHeaderSortState();
69373         this.syncFocusEl(0);
69374     },
69375
69376     // private
69377     onClear : function(){
69378         this.refresh();
69379         this.syncFocusEl(0);
69380     },
69381
69382     // private
69383     onUpdate : function(ds, record){
69384         this.refreshRow(record);
69385     },
69386
69387     // private
69388     onAdd : function(ds, records, index){
69389         this.insertRows(ds, index, index + (records.length-1));
69390     },
69391
69392     // private
69393     onRemove : function(ds, record, index, isUpdate){
69394         if(isUpdate !== true){
69395             this.fireEvent('beforerowremoved', this, index, record);
69396         }
69397         this.removeRow(index);
69398         if(isUpdate !== true){
69399             this.processRows(index);
69400             this.applyEmptyText();
69401             this.fireEvent('rowremoved', this, index, record);
69402         }
69403     },
69404
69405     // private
69406     onLoad : function(){
69407         if (Ext.isGecko){
69408             if (!this.scrollToTopTask) {
69409                 this.scrollToTopTask = new Ext.util.DelayedTask(this.scrollToTop, this);
69410             }
69411             this.scrollToTopTask.delay(1);
69412         }else{
69413             this.scrollToTop();
69414         }
69415     },
69416
69417     // private
69418     onColWidthChange : function(cm, col, width){
69419         this.updateColumnWidth(col, width);
69420     },
69421
69422     // private
69423     onHeaderChange : function(cm, col, text){
69424         this.updateHeaders();
69425     },
69426
69427     // private
69428     onHiddenChange : function(cm, col, hidden){
69429         this.updateColumnHidden(col, hidden);
69430     },
69431
69432     // private
69433     onColumnMove : function(cm, oldIndex, newIndex){
69434         this.indexMap = null;
69435         var s = this.getScrollState();
69436         this.refresh(true);
69437         this.restoreScroll(s);
69438         this.afterMove(newIndex);
69439         this.grid.fireEvent('columnmove', oldIndex, newIndex);
69440     },
69441
69442     // private
69443     onColConfigChange : function(){
69444         delete this.lastViewWidth;
69445         this.indexMap = null;
69446         this.refresh(true);
69447     },
69448
69449     /* -------------------- UI Events and Handlers ------------------------------ */
69450     // private
69451     initUI : function(grid){
69452         grid.on('headerclick', this.onHeaderClick, this);
69453     },
69454
69455     // private
69456     initEvents : function(){
69457     },
69458
69459     // private
69460     onHeaderClick : function(g, index){
69461         if(this.headersDisabled || !this.cm.isSortable(index)){
69462             return;
69463         }
69464         g.stopEditing(true);
69465         g.store.sort(this.cm.getDataIndex(index));
69466     },
69467
69468     // private
69469     onRowOver : function(e, t){
69470         var row;
69471         if((row = this.findRowIndex(t)) !== false){
69472             this.addRowClass(row, 'x-grid3-row-over');
69473         }
69474     },
69475
69476     // private
69477     onRowOut : function(e, t){
69478         var row;
69479         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
69480             this.removeRowClass(row, 'x-grid3-row-over');
69481         }
69482     },
69483
69484     // private
69485     handleWheel : function(e){
69486         e.stopPropagation();
69487     },
69488
69489     // private
69490     onRowSelect : function(row){
69491         this.addRowClass(row, this.selectedRowClass);
69492     },
69493
69494     // private
69495     onRowDeselect : function(row){
69496         this.removeRowClass(row, this.selectedRowClass);
69497     },
69498
69499     // private
69500     onCellSelect : function(row, col){
69501         var cell = this.getCell(row, col);
69502         if(cell){
69503             this.fly(cell).addClass('x-grid3-cell-selected');
69504         }
69505     },
69506
69507     // private
69508     onCellDeselect : function(row, col){
69509         var cell = this.getCell(row, col);
69510         if(cell){
69511             this.fly(cell).removeClass('x-grid3-cell-selected');
69512         }
69513     },
69514
69515     // private
69516     onColumnSplitterMoved : function(i, w){
69517         this.userResized = true;
69518         var cm = this.grid.colModel;
69519         cm.setColumnWidth(i, w, true);
69520
69521         if(this.forceFit){
69522             this.fitColumns(true, false, i);
69523             this.updateAllColumnWidths();
69524         }else{
69525             this.updateColumnWidth(i, w);
69526             this.syncHeaderScroll();
69527         }
69528
69529         this.grid.fireEvent('columnresize', i, w);
69530     },
69531
69532     // private
69533     handleHdMenuClick : function(item){
69534         var index = this.hdCtxIndex,
69535             cm = this.cm,
69536             ds = this.ds,
69537             id = item.getItemId();
69538         switch(id){
69539             case 'asc':
69540                 ds.sort(cm.getDataIndex(index), 'ASC');
69541                 break;
69542             case 'desc':
69543                 ds.sort(cm.getDataIndex(index), 'DESC');
69544                 break;
69545             default:
69546                 index = cm.getIndexById(id.substr(4));
69547                 if(index != -1){
69548                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
69549                         this.onDenyColumnHide();
69550                         return false;
69551                     }
69552                     cm.setHidden(index, item.checked);
69553                 }
69554         }
69555         return true;
69556     },
69557
69558     // private
69559     isHideableColumn : function(c){
69560         return !c.hidden;
69561     },
69562
69563     // private
69564     beforeColMenuShow : function(){
69565         var cm = this.cm,  colCount = cm.getColumnCount();
69566         this.colMenu.removeAll();
69567         for(var i = 0; i < colCount; i++){
69568             if(cm.config[i].hideable !== false){
69569                 this.colMenu.add(new Ext.menu.CheckItem({
69570                     itemId: 'col-'+cm.getColumnId(i),
69571                     text: cm.getColumnHeader(i),
69572                     checked: !cm.isHidden(i),
69573                     hideOnClick:false,
69574                     disabled: cm.config[i].hideable === false
69575                 }));
69576             }
69577         }
69578     },
69579
69580     // private
69581     handleHdDown : function(e, t){
69582         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
69583             e.stopEvent();
69584             var hd = this.findHeaderCell(t);
69585             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
69586             var index = this.getCellIndex(hd);
69587             this.hdCtxIndex = index;
69588             var ms = this.hmenu.items, cm = this.cm;
69589             ms.get('asc').setDisabled(!cm.isSortable(index));
69590             ms.get('desc').setDisabled(!cm.isSortable(index));
69591             this.hmenu.on('hide', function(){
69592                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
69593             }, this, {single:true});
69594             this.hmenu.show(t, 'tl-bl?');
69595         }
69596     },
69597
69598     // private
69599     handleHdOver : function(e, t){
69600         var hd = this.findHeaderCell(t);
69601         if(hd && !this.headersDisabled){
69602             this.activeHdRef = t;
69603             this.activeHdIndex = this.getCellIndex(hd);
69604             var fly = this.fly(hd);
69605             this.activeHdRegion = fly.getRegion();
69606             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
69607                 fly.addClass('x-grid3-hd-over');
69608                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
69609                 if(this.activeHdBtn){
69610                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
69611                 }
69612             }
69613         }
69614     },
69615
69616     // private
69617     handleHdMove : function(e, t){
69618         var hd = this.findHeaderCell(this.activeHdRef);
69619         if(hd && !this.headersDisabled){
69620             var hw = this.splitHandleWidth || 5,
69621                 r = this.activeHdRegion,
69622                 x = e.getPageX(),
69623                 ss = hd.style,
69624                 cur = '';
69625             if(this.grid.enableColumnResize !== false){
69626                 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
69627                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
69628                 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
69629                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
69630                 }
69631             }
69632             ss.cursor = cur;
69633         }
69634     },
69635
69636     // private
69637     handleHdOut : function(e, t){
69638         var hd = this.findHeaderCell(t);
69639         if(hd && (!Ext.isIE || !e.within(hd, true))){
69640             this.activeHdRef = null;
69641             this.fly(hd).removeClass('x-grid3-hd-over');
69642             hd.style.cursor = '';
69643         }
69644     },
69645
69646     // private
69647     hasRows : function(){
69648         var fc = this.mainBody.dom.firstChild;
69649         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
69650     },
69651
69652     // back compat
69653     bind : function(d, c){
69654         this.initData(d, c);
69655     }
69656 });
69657
69658
69659 // private
69660 // This is a support class used internally by the Grid components
69661 Ext.grid.GridView.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
69662     
69663     constructor: function(grid, hd){
69664         this.grid = grid;
69665         this.view = grid.getView();
69666         this.marker = this.view.resizeMarker;
69667         this.proxy = this.view.resizeProxy;
69668         Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
69669             'gridSplitters' + this.grid.getGridEl().id, {
69670             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
69671         });
69672         this.scroll = false;
69673         this.hw = this.view.splitHandleWidth || 5;
69674     },
69675
69676     b4StartDrag : function(x, y){
69677         this.dragHeadersDisabled = this.view.headersDisabled;
69678         this.view.headersDisabled = true;
69679         var h = this.view.mainWrap.getHeight();
69680         this.marker.setHeight(h);
69681         this.marker.show();
69682         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
69683         this.proxy.setHeight(h);
69684         var w = this.cm.getColumnWidth(this.cellIndex),
69685             minw = Math.max(w-this.grid.minColumnWidth, 0);
69686         this.resetConstraints();
69687         this.setXConstraint(minw, 1000);
69688         this.setYConstraint(0, 0);
69689         this.minX = x - minw;
69690         this.maxX = x + 1000;
69691         this.startPos = x;
69692         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
69693     },
69694
69695     allowHeaderDrag : function(e){
69696         return true;
69697     },
69698
69699     handleMouseDown : function(e){
69700         var t = this.view.findHeaderCell(e.getTarget());
69701         if(t && this.allowHeaderDrag(e)){
69702             var xy = this.view.fly(t).getXY(), 
69703                 x = xy[0], 
69704                 y = xy[1];
69705                 exy = e.getXY(), ex = exy[0],
69706                 w = t.offsetWidth, adjust = false;
69707                 
69708             if((ex - x) <= this.hw){
69709                 adjust = -1;
69710             }else if((x+w) - ex <= this.hw){
69711                 adjust = 0;
69712             }
69713             if(adjust !== false){
69714                 this.cm = this.grid.colModel;
69715                 var ci = this.view.getCellIndex(t);
69716                 if(adjust == -1){
69717                   if (ci + adjust < 0) {
69718                     return;
69719                   }
69720                     while(this.cm.isHidden(ci+adjust)){
69721                         --adjust;
69722                         if(ci+adjust < 0){
69723                             return;
69724                         }
69725                     }
69726                 }
69727                 this.cellIndex = ci+adjust;
69728                 this.split = t.dom;
69729                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
69730                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
69731                 }
69732             }else if(this.view.columnDrag){
69733                 this.view.columnDrag.callHandleMouseDown(e);
69734             }
69735         }
69736     },
69737
69738     endDrag : function(e){
69739         this.marker.hide();
69740         var v = this.view,
69741             endX = Math.max(this.minX, e.getPageX()),
69742             diff = endX - this.startPos,
69743             disabled = this.dragHeadersDisabled;
69744             
69745         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
69746         setTimeout(function(){
69747             v.headersDisabled = disabled;
69748         }, 50);
69749     },
69750
69751     autoOffset : function(){
69752         this.setDelta(0,0);
69753     }
69754 });
69755 // private
69756 // This is a support class used internally by the Grid components
69757 Ext.grid.HeaderDragZone = Ext.extend(Ext.dd.DragZone, {
69758     maxDragWidth: 120,
69759     
69760     constructor : function(grid, hd, hd2){
69761         this.grid = grid;
69762         this.view = grid.getView();
69763         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
69764         Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);
69765         if(hd2){
69766             this.setHandleElId(Ext.id(hd));
69767             this.setOuterHandleElId(Ext.id(hd2));
69768         }
69769         this.scroll = false;
69770     },
69771     
69772     getDragData : function(e){
69773         var t = Ext.lib.Event.getTarget(e),
69774             h = this.view.findHeaderCell(t);
69775         if(h){
69776             return {ddel: h.firstChild, header:h};
69777         }
69778         return false;
69779     },
69780
69781     onInitDrag : function(e){
69782         // keep the value here so we can restore it;
69783         this.dragHeadersDisabled = this.view.headersDisabled;
69784         this.view.headersDisabled = true;
69785         var clone = this.dragData.ddel.cloneNode(true);
69786         clone.id = Ext.id();
69787         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
69788         this.proxy.update(clone);
69789         return true;
69790     },
69791
69792     afterValidDrop : function(){
69793         this.completeDrop();
69794     },
69795
69796     afterInvalidDrop : function(){
69797         this.completeDrop();
69798     },
69799     
69800     completeDrop: function(){
69801         var v = this.view,
69802             disabled = this.dragHeadersDisabled;
69803         setTimeout(function(){
69804             v.headersDisabled = disabled;
69805         }, 50);
69806     }
69807 });
69808
69809 // private
69810 // This is a support class used internally by the Grid components
69811 Ext.grid.HeaderDropZone = Ext.extend(Ext.dd.DropZone, {
69812     proxyOffsets : [-4, -9],
69813     fly: Ext.Element.fly,
69814     
69815     constructor : function(grid, hd, hd2){
69816         this.grid = grid;
69817         this.view = grid.getView();
69818         // split the proxies so they don't interfere with mouse events
69819         this.proxyTop = Ext.DomHelper.append(document.body, {
69820             cls:"col-move-top", html:"&#160;"
69821         }, true);
69822         this.proxyBottom = Ext.DomHelper.append(document.body, {
69823             cls:"col-move-bottom", html:"&#160;"
69824         }, true);
69825         this.proxyTop.hide = this.proxyBottom.hide = function(){
69826             this.setLeftTop(-100,-100);
69827             this.setStyle("visibility", "hidden");
69828         };
69829         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
69830         // temporarily disabled
69831         //Ext.dd.ScrollManager.register(this.view.scroller.dom);
69832         Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
69833     },
69834
69835     getTargetFromEvent : function(e){
69836         var t = Ext.lib.Event.getTarget(e),
69837             cindex = this.view.findCellIndex(t);
69838         if(cindex !== false){
69839             return this.view.getHeaderCell(cindex);
69840         }
69841     },
69842
69843     nextVisible : function(h){
69844         var v = this.view, cm = this.grid.colModel;
69845         h = h.nextSibling;
69846         while(h){
69847             if(!cm.isHidden(v.getCellIndex(h))){
69848                 return h;
69849             }
69850             h = h.nextSibling;
69851         }
69852         return null;
69853     },
69854
69855     prevVisible : function(h){
69856         var v = this.view, cm = this.grid.colModel;
69857         h = h.prevSibling;
69858         while(h){
69859             if(!cm.isHidden(v.getCellIndex(h))){
69860                 return h;
69861             }
69862             h = h.prevSibling;
69863         }
69864         return null;
69865     },
69866
69867     positionIndicator : function(h, n, e){
69868         var x = Ext.lib.Event.getPageX(e),
69869             r = Ext.lib.Dom.getRegion(n.firstChild),
69870             px, 
69871             pt, 
69872             py = r.top + this.proxyOffsets[1];
69873         if((r.right - x) <= (r.right-r.left)/2){
69874             px = r.right+this.view.borderWidth;
69875             pt = "after";
69876         }else{
69877             px = r.left;
69878             pt = "before";
69879         }
69880
69881         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){
69882             return false;
69883         }
69884
69885         px +=  this.proxyOffsets[0];
69886         this.proxyTop.setLeftTop(px, py);
69887         this.proxyTop.show();
69888         if(!this.bottomOffset){
69889             this.bottomOffset = this.view.mainHd.getHeight();
69890         }
69891         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
69892         this.proxyBottom.show();
69893         return pt;
69894     },
69895
69896     onNodeEnter : function(n, dd, e, data){
69897         if(data.header != n){
69898             this.positionIndicator(data.header, n, e);
69899         }
69900     },
69901
69902     onNodeOver : function(n, dd, e, data){
69903         var result = false;
69904         if(data.header != n){
69905             result = this.positionIndicator(data.header, n, e);
69906         }
69907         if(!result){
69908             this.proxyTop.hide();
69909             this.proxyBottom.hide();
69910         }
69911         return result ? this.dropAllowed : this.dropNotAllowed;
69912     },
69913
69914     onNodeOut : function(n, dd, e, data){
69915         this.proxyTop.hide();
69916         this.proxyBottom.hide();
69917     },
69918
69919     onNodeDrop : function(n, dd, e, data){
69920         var h = data.header;
69921         if(h != n){
69922             var cm = this.grid.colModel,
69923                 x = Ext.lib.Event.getPageX(e),
69924                 r = Ext.lib.Dom.getRegion(n.firstChild),
69925                 pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before",
69926                 oldIndex = this.view.getCellIndex(h),
69927                 newIndex = this.view.getCellIndex(n);
69928             if(pt == "after"){
69929                 newIndex++;
69930             }
69931             if(oldIndex < newIndex){
69932                 newIndex--;
69933             }
69934             cm.moveColumn(oldIndex, newIndex);
69935             return true;
69936         }
69937         return false;
69938     }
69939 });
69940
69941 Ext.grid.GridView.ColumnDragZone = Ext.extend(Ext.grid.HeaderDragZone, {
69942     
69943     constructor : function(grid, hd){
69944         Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
69945         this.proxy.el.addClass('x-grid3-col-dd');
69946     },
69947     
69948     handleMouseDown : function(e){
69949     },
69950
69951     callHandleMouseDown : function(e){
69952         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
69953     }
69954 });// private
69955 // This is a support class used internally by the Grid components
69956 Ext.grid.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
69957     fly: Ext.Element.fly,
69958     
69959     constructor : function(grid, hd, hd2){
69960         this.grid = grid;
69961         this.view = grid.getView();
69962         this.proxy = this.view.resizeProxy;
69963         Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
69964             "gridSplitters" + this.grid.getGridEl().id, {
69965             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
69966         });
69967         this.setHandleElId(Ext.id(hd));
69968         this.setOuterHandleElId(Ext.id(hd2));
69969         this.scroll = false;
69970     },
69971
69972     b4StartDrag : function(x, y){
69973         this.view.headersDisabled = true;
69974         this.proxy.setHeight(this.view.mainWrap.getHeight());
69975         var w = this.cm.getColumnWidth(this.cellIndex);
69976         var minw = Math.max(w-this.grid.minColumnWidth, 0);
69977         this.resetConstraints();
69978         this.setXConstraint(minw, 1000);
69979         this.setYConstraint(0, 0);
69980         this.minX = x - minw;
69981         this.maxX = x + 1000;
69982         this.startPos = x;
69983         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
69984     },
69985
69986
69987     handleMouseDown : function(e){
69988         var ev = Ext.EventObject.setEvent(e);
69989         var t = this.fly(ev.getTarget());
69990         if(t.hasClass("x-grid-split")){
69991             this.cellIndex = this.view.getCellIndex(t.dom);
69992             this.split = t.dom;
69993             this.cm = this.grid.colModel;
69994             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
69995                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
69996             }
69997         }
69998     },
69999
70000     endDrag : function(e){
70001         this.view.headersDisabled = false;
70002         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
70003         var diff = endX - this.startPos;
70004         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
70005     },
70006
70007     autoOffset : function(){
70008         this.setDelta(0,0);
70009     }
70010 });/**
70011  * @class Ext.grid.GridDragZone
70012  * @extends Ext.dd.DragZone
70013  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
70014  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
70015  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
70016  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
70017  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
70018  * to process the {@link #getDragData data} which is provided.
70019  */
70020 Ext.grid.GridDragZone = function(grid, config){
70021     this.view = grid.getView();
70022     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
70023     this.scroll = false;
70024     this.grid = grid;
70025     this.ddel = document.createElement('div');
70026     this.ddel.className = 'x-grid-dd-wrap';
70027 };
70028
70029 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
70030     ddGroup : "GridDD",
70031
70032     /**
70033      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
70034      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
70035      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
70036      * <p>The data object contains the following properties:<ul>
70037      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
70038      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
70039      * <li><b>rowIndex</b> : Number<div class="sub-desc">The index of the row which receieved the mousedown gesture which triggered the drag.</div></li>
70040      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
70041      * </ul></p>
70042      */
70043     getDragData : function(e){
70044         var t = Ext.lib.Event.getTarget(e);
70045         var rowIndex = this.view.findRowIndex(t);
70046         if(rowIndex !== false){
70047             var sm = this.grid.selModel;
70048             if(!sm.isSelected(rowIndex) || e.hasModifier()){
70049                 sm.handleMouseDown(this.grid, rowIndex, e);
70050             }
70051             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
70052         }
70053         return false;
70054     },
70055
70056     /**
70057      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
70058      * of the data being dragged.</p>
70059      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
70060      */
70061     onInitDrag : function(e){
70062         var data = this.dragData;
70063         this.ddel.innerHTML = this.grid.getDragDropText();
70064         this.proxy.update(this.ddel);
70065         // fire start drag?
70066     },
70067
70068     /**
70069      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
70070      * the selected rows to show that they have not been dragged.
70071      */
70072     afterRepair : function(){
70073         this.dragging = false;
70074     },
70075
70076     /**
70077      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
70078      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
70079      * @param {EventObject} e The mouse up event
70080      * @return {Array} The xy location (e.g. [100, 200])
70081      */
70082     getRepairXY : function(e, data){
70083         return false;
70084     },
70085
70086     onEndDrag : function(data, e){
70087         // fire end drag?
70088     },
70089
70090     onValidDrop : function(dd, e, id){
70091         // fire drag drop?
70092         this.hideProxy();
70093     },
70094
70095     beforeInvalidDrop : function(e, id){
70096
70097     }
70098 });
70099 /**
70100  * @class Ext.grid.ColumnModel
70101  * @extends Ext.util.Observable
70102  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
70103  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
70104  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
70105  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
70106  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
70107  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
70108  * <pre><code>
70109 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
70110  * </code></pre>
70111  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
70112  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
70113  * each record in the store is indexed into the ColumnModel.</p>
70114  * <p>There are two ways to initialize the ColumnModel class:</p>
70115  * <p><u>Initialization Method 1: an Array</u></p>
70116 <pre><code>
70117  var colModel = new Ext.grid.ColumnModel([
70118     { header: "Ticker", width: 60, sortable: true},
70119     { header: "Company Name", width: 150, sortable: true, id: 'company'},
70120     { header: "Market Cap.", width: 100, sortable: true},
70121     { header: "$ Sales", width: 100, sortable: true, renderer: money},
70122     { header: "Employees", width: 100, sortable: true, resizable: false}
70123  ]);
70124  </code></pre>
70125  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
70126  * objects to define the initial layout / display of the columns in the Grid. The order of each
70127  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
70128  * order of the column display.  A Column's display may be initially hidden using the
70129  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
70130  * header menu).  Fields that are not included in the ColumnModel will not be displayable at all.</p>
70131  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
70132  * {@link Ext.data.Store Store} the column draws its data from is configured through the
70133  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
70134  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
70135  * example above) it will use the column configuration's index in the Array as the index.</p>
70136  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
70137  * <p><u>Initialization Method 2: an Object</u></p>
70138  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
70139  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
70140  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
70141  * for all columns, e.g.:</p><pre><code>
70142  var colModel = new Ext.grid.ColumnModel({
70143     columns: [
70144         { header: "Ticker", width: 60, menuDisabled: false},
70145         { header: "Company Name", width: 150, id: 'company'},
70146         { header: "Market Cap."},
70147         { header: "$ Sales", renderer: money},
70148         { header: "Employees", resizable: false}
70149     ],
70150     defaults: {
70151         sortable: true,
70152         menuDisabled: true,
70153         width: 100
70154     },
70155     listeners: {
70156         {@link #hiddenchange}: function(cm, colIndex, hidden) {
70157             saveConfig(colIndex, hidden);
70158         }
70159     }
70160 });
70161  </code></pre>
70162  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
70163  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
70164  * option. This column could be styled by including the following css:</p><pre><code>
70165  //add this css *after* the core css is loaded
70166 .x-grid3-td-company {
70167     color: red; // entire column will have red font
70168 }
70169 // modify the header row only, adding an icon to the column header
70170 .x-grid3-hd-company {
70171     background: transparent
70172         url(../../resources/images/icons/silk/building.png)
70173         no-repeat 3px 3px ! important;
70174         padding-left:20px;
70175 }
70176  </code></pre>
70177  * Note that the "Company Name" column could be specified as the
70178  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
70179  * @constructor
70180  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
70181  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
70182  */
70183 Ext.grid.ColumnModel = Ext.extend(Ext.util.Observable, {
70184     /**
70185      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
70186      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
70187      * <tt><b>{@link #defaults}</b></tt> config property.
70188      */
70189     defaultWidth: 100,
70190     /**
70191      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
70192      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
70193      * through the <tt><b>{@link #defaults}</b></tt> config property.
70194      */
70195     defaultSortable: false,
70196     /**
70197      * @cfg {Array} columns An Array of object literals.  The config options defined by
70198      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
70199      * individual column definition.
70200      */
70201     /**
70202      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
70203      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
70204      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
70205      */
70206
70207     constructor : function(config){
70208         /**
70209              * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
70210              * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
70211              * be specified.
70212              * @property config
70213              * @type Array
70214              */
70215             if(config.columns){
70216                 Ext.apply(this, config);
70217                 this.setConfig(config.columns, true);
70218             }else{
70219                 this.setConfig(config, true);
70220             }
70221             this.addEvents(
70222                 /**
70223                  * @event widthchange
70224                  * Fires when the width of a column is programmaticially changed using
70225                  * <code>{@link #setColumnWidth}</code>.
70226                  * Note internal resizing suppresses the event from firing. See also
70227                  * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
70228                  * @param {ColumnModel} this
70229                  * @param {Number} columnIndex The column index
70230                  * @param {Number} newWidth The new width
70231                  */
70232                 "widthchange",
70233                 /**
70234                  * @event headerchange
70235                  * Fires when the text of a header changes.
70236                  * @param {ColumnModel} this
70237                  * @param {Number} columnIndex The column index
70238                  * @param {String} newText The new header text
70239                  */
70240                 "headerchange",
70241                 /**
70242                  * @event hiddenchange
70243                  * Fires when a column is hidden or "unhidden".
70244                  * @param {ColumnModel} this
70245                  * @param {Number} columnIndex The column index
70246                  * @param {Boolean} hidden true if hidden, false otherwise
70247                  */
70248                 "hiddenchange",
70249                 /**
70250                  * @event columnmoved
70251                  * Fires when a column is moved.
70252                  * @param {ColumnModel} this
70253                  * @param {Number} oldIndex
70254                  * @param {Number} newIndex
70255                  */
70256                 "columnmoved",
70257                 /**
70258                  * @event configchange
70259                  * Fires when the configuration is changed
70260                  * @param {ColumnModel} this
70261                  */
70262                 "configchange"
70263             );
70264             Ext.grid.ColumnModel.superclass.constructor.call(this);
70265     },
70266
70267     /**
70268      * Returns the id of the column at the specified index.
70269      * @param {Number} index The column index
70270      * @return {String} the id
70271      */
70272     getColumnId : function(index){
70273         return this.config[index].id;
70274     },
70275
70276     getColumnAt : function(index){
70277         return this.config[index];
70278     },
70279
70280     /**
70281      * <p>Reconfigures this column model according to the passed Array of column definition objects.
70282      * For a description of the individual properties of a column definition object, see the
70283      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
70284      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
70285      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
70286      * @param {Array} config Array of Column definition objects.
70287      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
70288      * and destroys existing editors.
70289      */
70290     setConfig : function(config, initial){
70291         var i, c, len;
70292         if(!initial){ // cleanup
70293             delete this.totalWidth;
70294             for(i = 0, len = this.config.length; i < len; i++){
70295                 c = this.config[i];
70296                 if(c.setEditor){
70297                     //check here, in case we have a special column like a CheckboxSelectionModel
70298                     c.setEditor(null);
70299                 }
70300             }
70301         }
70302
70303         // backward compatibility
70304         this.defaults = Ext.apply({
70305             width: this.defaultWidth,
70306             sortable: this.defaultSortable
70307         }, this.defaults);
70308
70309         this.config = config;
70310         this.lookup = {};
70311
70312         for(i = 0, len = config.length; i < len; i++){
70313             c = Ext.applyIf(config[i], this.defaults);
70314             // if no id, create one using column's ordinal position
70315             if(Ext.isEmpty(c.id)){
70316                 c.id = i;
70317             }
70318             if(!c.isColumn){
70319                 var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
70320                 c = new Cls(c);
70321                 config[i] = c;
70322             }
70323             this.lookup[c.id] = c;
70324         }
70325         if(!initial){
70326             this.fireEvent('configchange', this);
70327         }
70328     },
70329
70330     /**
70331      * Returns the column for a specified id.
70332      * @param {String} id The column id
70333      * @return {Object} the column
70334      */
70335     getColumnById : function(id){
70336         return this.lookup[id];
70337     },
70338
70339     /**
70340      * Returns the index for a specified column id.
70341      * @param {String} id The column id
70342      * @return {Number} the index, or -1 if not found
70343      */
70344     getIndexById : function(id){
70345         for(var i = 0, len = this.config.length; i < len; i++){
70346             if(this.config[i].id == id){
70347                 return i;
70348             }
70349         }
70350         return -1;
70351     },
70352
70353     /**
70354      * Moves a column from one position to another.
70355      * @param {Number} oldIndex The index of the column to move.
70356      * @param {Number} newIndex The position at which to reinsert the coolumn.
70357      */
70358     moveColumn : function(oldIndex, newIndex){
70359         var c = this.config[oldIndex];
70360         this.config.splice(oldIndex, 1);
70361         this.config.splice(newIndex, 0, c);
70362         this.dataMap = null;
70363         this.fireEvent("columnmoved", this, oldIndex, newIndex);
70364     },
70365
70366     /**
70367      * Returns the number of columns.
70368      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
70369      * @return {Number}
70370      */
70371     getColumnCount : function(visibleOnly){
70372         if(visibleOnly === true){
70373             var c = 0;
70374             for(var i = 0, len = this.config.length; i < len; i++){
70375                 if(!this.isHidden(i)){
70376                     c++;
70377                 }
70378             }
70379             return c;
70380         }
70381         return this.config.length;
70382     },
70383
70384     /**
70385      * Returns the column configs that return true by the passed function that is called
70386      * with (columnConfig, index)
70387 <pre><code>
70388 // returns an array of column config objects for all hidden columns
70389 var columns = grid.getColumnModel().getColumnsBy(function(c){
70390   return c.hidden;
70391 });
70392 </code></pre>
70393      * @param {Function} fn A function which, when passed a {@link Ext.grid.Column Column} object, must
70394      * return <code>true</code> if the column is to be included in the returned Array.
70395      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
70396      * is executed. Defaults to this ColumnModel.
70397      * @return {Array} result
70398      */
70399     getColumnsBy : function(fn, scope){
70400         var r = [];
70401         for(var i = 0, len = this.config.length; i < len; i++){
70402             var c = this.config[i];
70403             if(fn.call(scope||this, c, i) === true){
70404                 r[r.length] = c;
70405             }
70406         }
70407         return r;
70408     },
70409
70410     /**
70411      * Returns true if the specified column is sortable.
70412      * @param {Number} col The column index
70413      * @return {Boolean}
70414      */
70415     isSortable : function(col){
70416         return !!this.config[col].sortable;
70417     },
70418
70419     /**
70420      * Returns true if the specified column menu is disabled.
70421      * @param {Number} col The column index
70422      * @return {Boolean}
70423      */
70424     isMenuDisabled : function(col){
70425         return !!this.config[col].menuDisabled;
70426     },
70427
70428     /**
70429      * Returns the rendering (formatting) function defined for the column.
70430      * @param {Number} col The column index.
70431      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
70432      */
70433     getRenderer : function(col){
70434         if(!this.config[col].renderer){
70435             return Ext.grid.ColumnModel.defaultRenderer;
70436         }
70437         return this.config[col].renderer;
70438     },
70439
70440     getRendererScope : function(col){
70441         return this.config[col].scope;
70442     },
70443
70444     /**
70445      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
70446      * default formatting functions.
70447      * @param {Number} col The column index
70448      * @param {Function} fn The function to use to process the cell's raw data
70449      * to return HTML markup for the grid view. The render function is called with
70450      * the following parameters:<ul>
70451      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
70452      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
70453      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
70454      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
70455      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
70456      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
70457      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
70458      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
70459      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>
70460      */
70461     setRenderer : function(col, fn){
70462         this.config[col].renderer = fn;
70463     },
70464
70465     /**
70466      * Returns the width for the specified column.
70467      * @param {Number} col The column index
70468      * @return {Number}
70469      */
70470     getColumnWidth : function(col){
70471         return this.config[col].width;
70472     },
70473
70474     /**
70475      * Sets the width for a column.
70476      * @param {Number} col The column index
70477      * @param {Number} width The new width
70478      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
70479      * event. Defaults to false.
70480      */
70481     setColumnWidth : function(col, width, suppressEvent){
70482         this.config[col].width = width;
70483         this.totalWidth = null;
70484         if(!suppressEvent){
70485              this.fireEvent("widthchange", this, col, width);
70486         }
70487     },
70488
70489     /**
70490      * Returns the total width of all columns.
70491      * @param {Boolean} includeHidden True to include hidden column widths
70492      * @return {Number}
70493      */
70494     getTotalWidth : function(includeHidden){
70495         if(!this.totalWidth){
70496             this.totalWidth = 0;
70497             for(var i = 0, len = this.config.length; i < len; i++){
70498                 if(includeHidden || !this.isHidden(i)){
70499                     this.totalWidth += this.getColumnWidth(i);
70500                 }
70501             }
70502         }
70503         return this.totalWidth;
70504     },
70505
70506     /**
70507      * Returns the header for the specified column.
70508      * @param {Number} col The column index
70509      * @return {String}
70510      */
70511     getColumnHeader : function(col){
70512         return this.config[col].header;
70513     },
70514
70515     /**
70516      * Sets the header for a column.
70517      * @param {Number} col The column index
70518      * @param {String} header The new header
70519      */
70520     setColumnHeader : function(col, header){
70521         this.config[col].header = header;
70522         this.fireEvent("headerchange", this, col, header);
70523     },
70524
70525     /**
70526      * Returns the tooltip for the specified column.
70527      * @param {Number} col The column index
70528      * @return {String}
70529      */
70530     getColumnTooltip : function(col){
70531             return this.config[col].tooltip;
70532     },
70533     /**
70534      * Sets the tooltip for a column.
70535      * @param {Number} col The column index
70536      * @param {String} tooltip The new tooltip
70537      */
70538     setColumnTooltip : function(col, tooltip){
70539             this.config[col].tooltip = tooltip;
70540     },
70541
70542     /**
70543      * Returns the dataIndex for the specified column.
70544 <pre><code>
70545 // Get field name for the column
70546 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
70547 </code></pre>
70548      * @param {Number} col The column index
70549      * @return {String} The column's dataIndex
70550      */
70551     getDataIndex : function(col){
70552         return this.config[col].dataIndex;
70553     },
70554
70555     /**
70556      * Sets the dataIndex for a column.
70557      * @param {Number} col The column index
70558      * @param {String} dataIndex The new dataIndex
70559      */
70560     setDataIndex : function(col, dataIndex){
70561         this.config[col].dataIndex = dataIndex;
70562     },
70563
70564     /**
70565      * Finds the index of the first matching column for the given dataIndex.
70566      * @param {String} col The dataIndex to find
70567      * @return {Number} The column index, or -1 if no match was found
70568      */
70569     findColumnIndex : function(dataIndex){
70570         var c = this.config;
70571         for(var i = 0, len = c.length; i < len; i++){
70572             if(c[i].dataIndex == dataIndex){
70573                 return i;
70574             }
70575         }
70576         return -1;
70577     },
70578
70579     /**
70580      * Returns true if the cell is editable.
70581 <pre><code>
70582 var store = new Ext.data.Store({...});
70583 var colModel = new Ext.grid.ColumnModel({
70584   columns: [...],
70585   isCellEditable: function(col, row) {
70586     var record = store.getAt(row);
70587     if (record.get('readonly')) { // replace with your condition
70588       return false;
70589     }
70590     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
70591   }
70592 });
70593 var grid = new Ext.grid.GridPanel({
70594   store: store,
70595   colModel: colModel,
70596   ...
70597 });
70598 </code></pre>
70599      * @param {Number} colIndex The column index
70600      * @param {Number} rowIndex The row index
70601      * @return {Boolean}
70602      */
70603     isCellEditable : function(colIndex, rowIndex){
70604         var c = this.config[colIndex],
70605             ed = c.editable;
70606
70607         //force boolean
70608         return !!(ed || (!Ext.isDefined(ed) && c.editor));
70609     },
70610
70611     /**
70612      * Returns the editor defined for the cell/column.
70613      * @param {Number} colIndex The column index
70614      * @param {Number} rowIndex The row index
70615      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
70616      * the {@link Ext.form.Field Field} used to edit the cell.
70617      */
70618     getCellEditor : function(colIndex, rowIndex){
70619         return this.config[colIndex].getCellEditor(rowIndex);
70620     },
70621
70622     /**
70623      * Sets if a column is editable.
70624      * @param {Number} col The column index
70625      * @param {Boolean} editable True if the column is editable
70626      */
70627     setEditable : function(col, editable){
70628         this.config[col].editable = editable;
70629     },
70630
70631     /**
70632      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
70633      * <tt>false</tt> otherwise.
70634      * @param {Number} colIndex The column index
70635      * @return {Boolean}
70636      */
70637     isHidden : function(colIndex){
70638         return !!this.config[colIndex].hidden; // ensure returns boolean
70639     },
70640
70641     /**
70642      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
70643      * <tt>false</tt> otherwise.
70644      * @param {Number} colIndex The column index
70645      * @return {Boolean}
70646      */
70647     isFixed : function(colIndex){
70648         return !!this.config[colIndex].fixed;
70649     },
70650
70651     /**
70652      * Returns true if the column can be resized
70653      * @return {Boolean}
70654      */
70655     isResizable : function(colIndex){
70656         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
70657     },
70658     /**
70659      * Sets if a column is hidden.
70660 <pre><code>
70661 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
70662 </code></pre>
70663      * @param {Number} colIndex The column index
70664      * @param {Boolean} hidden True if the column is hidden
70665      */
70666     setHidden : function(colIndex, hidden){
70667         var c = this.config[colIndex];
70668         if(c.hidden !== hidden){
70669             c.hidden = hidden;
70670             this.totalWidth = null;
70671             this.fireEvent("hiddenchange", this, colIndex, hidden);
70672         }
70673     },
70674
70675     /**
70676      * Sets the editor for a column and destroys the prior editor.
70677      * @param {Number} col The column index
70678      * @param {Object} editor The editor object
70679      */
70680     setEditor : function(col, editor){
70681         this.config[col].setEditor(editor);
70682     },
70683
70684     /**
70685      * Destroys this column model by purging any event listeners, and removing any editors.
70686      */
70687     destroy : function(){
70688         var c;
70689         for(var i = 0, len = this.config.length; i < len; i++){
70690             c = this.config[i];
70691             if(c.setEditor){
70692                 c.setEditor(null);
70693             }
70694         }
70695         this.purgeListeners();
70696     }
70697 });
70698
70699 // private
70700 Ext.grid.ColumnModel.defaultRenderer = function(value){
70701     if(typeof value == "string" && value.length < 1){
70702         return "&#160;";
70703     }
70704     return value;
70705 };/**
70706  * @class Ext.grid.AbstractSelectionModel
70707  * @extends Ext.util.Observable
70708  * Abstract base class for grid SelectionModels.  It provides the interface that should be
70709  * implemented by descendant classes.  This class should not be directly instantiated.
70710  * @constructor
70711  */
70712 Ext.grid.AbstractSelectionModel = Ext.extend(Ext.util.Observable,  {
70713     /**
70714      * The GridPanel for which this SelectionModel is handling selection. Read-only.
70715      * @type Object
70716      * @property grid
70717      */
70718
70719     constructor : function(){
70720         this.locked = false;
70721         Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);
70722     },
70723
70724     /** @ignore Called by the grid automatically. Do not call directly. */
70725     init : function(grid){
70726         this.grid = grid;
70727         if(this.lockOnInit){
70728             delete this.lockOnInit;
70729             this.locked = false;
70730             this.lock();
70731         }
70732         this.initEvents();
70733     },
70734
70735     /**
70736      * Locks the selections.
70737      */
70738     lock : function(){
70739         if(!this.locked){
70740             this.locked = true;
70741             // If the grid has been set, then the view is already initialized.
70742             var g = this.grid;
70743             if(g){
70744                 g.getView().on({
70745                     scope: this,
70746                     beforerefresh: this.sortUnLock,
70747                     refresh: this.sortLock
70748                 });
70749             }else{
70750                 this.lockOnInit = true;
70751             }
70752         }
70753     },
70754
70755     // set the lock states before and after a view refresh
70756     sortLock : function() {
70757         this.locked = true;
70758     },
70759
70760     // set the lock states before and after a view refresh
70761     sortUnLock : function() {
70762         this.locked = false;
70763     },
70764
70765     /**
70766      * Unlocks the selections.
70767      */
70768     unlock : function(){
70769         if(this.locked){
70770             this.locked = false;
70771             var g = this.grid,
70772                 gv;
70773                 
70774             // If the grid has been set, then the view is already initialized.
70775             if(g){
70776                 gv = g.getView();
70777                 gv.un('beforerefresh', this.sortUnLock, this);
70778                 gv.un('refresh', this.sortLock, this);    
70779             }else{
70780                 delete this.lockOnInit;
70781             }
70782         }
70783     },
70784
70785     /**
70786      * Returns true if the selections are locked.
70787      * @return {Boolean}
70788      */
70789     isLocked : function(){
70790         return this.locked;
70791     },
70792
70793     destroy: function(){
70794         this.unlock();
70795         this.purgeListeners();
70796     }
70797 });/**
70798  * @class Ext.grid.RowSelectionModel
70799  * @extends Ext.grid.AbstractSelectionModel
70800  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
70801  * It supports multiple selections and keyboard selection/navigation. The objects stored
70802  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
70803  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
70804  * @constructor
70805  * @param {Object} config
70806  */
70807 Ext.grid.RowSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
70808     /**
70809      * @cfg {Boolean} singleSelect
70810      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
70811      * allowing multiple selections)
70812      */
70813     singleSelect : false,
70814     
70815     constructor : function(config){
70816         Ext.apply(this, config);
70817         this.selections = new Ext.util.MixedCollection(false, function(o){
70818             return o.id;
70819         });
70820
70821         this.last = false;
70822         this.lastActive = false;
70823
70824         this.addEvents(
70825                 /**
70826                  * @event selectionchange
70827                  * Fires when the selection changes
70828                  * @param {SelectionModel} this
70829                  */
70830                 'selectionchange',
70831                 /**
70832                  * @event beforerowselect
70833                  * Fires before a row is selected, return false to cancel the selection.
70834                  * @param {SelectionModel} this
70835                  * @param {Number} rowIndex The index to be selected
70836                  * @param {Boolean} keepExisting False if other selections will be cleared
70837                  * @param {Record} record The record to be selected
70838                  */
70839                 'beforerowselect',
70840                 /**
70841                  * @event rowselect
70842                  * Fires when a row is selected.
70843                  * @param {SelectionModel} this
70844                  * @param {Number} rowIndex The selected index
70845                  * @param {Ext.data.Record} r The selected record
70846                  */
70847                 'rowselect',
70848                 /**
70849                  * @event rowdeselect
70850                  * Fires when a row is deselected.  To prevent deselection
70851                  * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
70852                  * @param {SelectionModel} this
70853                  * @param {Number} rowIndex
70854                  * @param {Record} record
70855                  */
70856                 'rowdeselect'
70857         );
70858         Ext.grid.RowSelectionModel.superclass.constructor.call(this);
70859     },
70860
70861     /**
70862      * @cfg {Boolean} moveEditorOnEnter
70863      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
70864      * or the next row up when shift + enter keys are pressed.
70865      */
70866     // private
70867     initEvents : function(){
70868
70869         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
70870             this.grid.on('rowmousedown', this.handleMouseDown, this);
70871         }
70872
70873         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
70874             'up' : function(e){
70875                 if(!e.shiftKey || this.singleSelect){
70876                     this.selectPrevious(false);
70877                 }else if(this.last !== false && this.lastActive !== false){
70878                     var last = this.last;
70879                     this.selectRange(this.last,  this.lastActive-1);
70880                     this.grid.getView().focusRow(this.lastActive);
70881                     if(last !== false){
70882                         this.last = last;
70883                     }
70884                 }else{
70885                     this.selectFirstRow();
70886                 }
70887             },
70888             'down' : function(e){
70889                 if(!e.shiftKey || this.singleSelect){
70890                     this.selectNext(false);
70891                 }else if(this.last !== false && this.lastActive !== false){
70892                     var last = this.last;
70893                     this.selectRange(this.last,  this.lastActive+1);
70894                     this.grid.getView().focusRow(this.lastActive);
70895                     if(last !== false){
70896                         this.last = last;
70897                     }
70898                 }else{
70899                     this.selectFirstRow();
70900                 }
70901             },
70902             scope: this
70903         });
70904
70905         this.grid.getView().on({
70906             scope: this,
70907             refresh: this.onRefresh,
70908             rowupdated: this.onRowUpdated,
70909             rowremoved: this.onRemove
70910         });
70911     },
70912
70913     // private
70914     onRefresh : function(){
70915         var ds = this.grid.store, index;
70916         var s = this.getSelections();
70917         this.clearSelections(true);
70918         for(var i = 0, len = s.length; i < len; i++){
70919             var r = s[i];
70920             if((index = ds.indexOfId(r.id)) != -1){
70921                 this.selectRow(index, true);
70922             }
70923         }
70924         if(s.length != this.selections.getCount()){
70925             this.fireEvent('selectionchange', this);
70926         }
70927     },
70928
70929     // private
70930     onRemove : function(v, index, r){
70931         if(this.selections.remove(r) !== false){
70932             this.fireEvent('selectionchange', this);
70933         }
70934     },
70935
70936     // private
70937     onRowUpdated : function(v, index, r){
70938         if(this.isSelected(r)){
70939             v.onRowSelect(index);
70940         }
70941     },
70942
70943     /**
70944      * Select records.
70945      * @param {Array} records The records to select
70946      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
70947      */
70948     selectRecords : function(records, keepExisting){
70949         if(!keepExisting){
70950             this.clearSelections();
70951         }
70952         var ds = this.grid.store;
70953         for(var i = 0, len = records.length; i < len; i++){
70954             this.selectRow(ds.indexOf(records[i]), true);
70955         }
70956     },
70957
70958     /**
70959      * Gets the number of selected rows.
70960      * @return {Number}
70961      */
70962     getCount : function(){
70963         return this.selections.length;
70964     },
70965
70966     /**
70967      * Selects the first row in the grid.
70968      */
70969     selectFirstRow : function(){
70970         this.selectRow(0);
70971     },
70972
70973     /**
70974      * Select the last row.
70975      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
70976      */
70977     selectLastRow : function(keepExisting){
70978         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
70979     },
70980
70981     /**
70982      * Selects the row immediately following the last selected row.
70983      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
70984      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
70985      */
70986     selectNext : function(keepExisting){
70987         if(this.hasNext()){
70988             this.selectRow(this.last+1, keepExisting);
70989             this.grid.getView().focusRow(this.last);
70990             return true;
70991         }
70992         return false;
70993     },
70994
70995     /**
70996      * Selects the row that precedes the last selected row.
70997      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
70998      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
70999      */
71000     selectPrevious : function(keepExisting){
71001         if(this.hasPrevious()){
71002             this.selectRow(this.last-1, keepExisting);
71003             this.grid.getView().focusRow(this.last);
71004             return true;
71005         }
71006         return false;
71007     },
71008
71009     /**
71010      * Returns true if there is a next record to select
71011      * @return {Boolean}
71012      */
71013     hasNext : function(){
71014         return this.last !== false && (this.last+1) < this.grid.store.getCount();
71015     },
71016
71017     /**
71018      * Returns true if there is a previous record to select
71019      * @return {Boolean}
71020      */
71021     hasPrevious : function(){
71022         return !!this.last;
71023     },
71024
71025
71026     /**
71027      * Returns the selected records
71028      * @return {Array} Array of selected records
71029      */
71030     getSelections : function(){
71031         return [].concat(this.selections.items);
71032     },
71033
71034     /**
71035      * Returns the first selected record.
71036      * @return {Record}
71037      */
71038     getSelected : function(){
71039         return this.selections.itemAt(0);
71040     },
71041
71042     /**
71043      * Calls the passed function with each selection. If the function returns
71044      * <tt>false</tt>, iteration is stopped and this function returns
71045      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
71046      * @param {Function} fn The function to call upon each iteration. It is passed the selected {@link Ext.data.Record Record}.
71047      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this RowSelectionModel.
71048      * @return {Boolean} true if all selections were iterated
71049      */
71050     each : function(fn, scope){
71051         var s = this.getSelections();
71052         for(var i = 0, len = s.length; i < len; i++){
71053             if(fn.call(scope || this, s[i], i) === false){
71054                 return false;
71055             }
71056         }
71057         return true;
71058     },
71059
71060     /**
71061      * Clears all selections if the selection model
71062      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
71063      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
71064      * conditional checks and events described in {@link #deselectRow}.
71065      */
71066     clearSelections : function(fast){
71067         if(this.isLocked()){
71068             return;
71069         }
71070         if(fast !== true){
71071             var ds = this.grid.store;
71072             var s = this.selections;
71073             s.each(function(r){
71074                 this.deselectRow(ds.indexOfId(r.id));
71075             }, this);
71076             s.clear();
71077         }else{
71078             this.selections.clear();
71079         }
71080         this.last = false;
71081     },
71082
71083
71084     /**
71085      * Selects all rows if the selection model
71086      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
71087      */
71088     selectAll : function(){
71089         if(this.isLocked()){
71090             return;
71091         }
71092         this.selections.clear();
71093         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
71094             this.selectRow(i, true);
71095         }
71096     },
71097
71098     /**
71099      * Returns <tt>true</tt> if there is a selection.
71100      * @return {Boolean}
71101      */
71102     hasSelection : function(){
71103         return this.selections.length > 0;
71104     },
71105
71106     /**
71107      * Returns <tt>true</tt> if the specified row is selected.
71108      * @param {Number/Record} index The record or index of the record to check
71109      * @return {Boolean}
71110      */
71111     isSelected : function(index){
71112         var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
71113         return (r && this.selections.key(r.id) ? true : false);
71114     },
71115
71116     /**
71117      * Returns <tt>true</tt> if the specified record id is selected.
71118      * @param {String} id The id of record to check
71119      * @return {Boolean}
71120      */
71121     isIdSelected : function(id){
71122         return (this.selections.key(id) ? true : false);
71123     },
71124
71125     // private
71126     handleMouseDown : function(g, rowIndex, e){
71127         if(e.button !== 0 || this.isLocked()){
71128             return;
71129         }
71130         var view = this.grid.getView();
71131         if(e.shiftKey && !this.singleSelect && this.last !== false){
71132             var last = this.last;
71133             this.selectRange(last, rowIndex, e.ctrlKey);
71134             this.last = last; // reset the last
71135             view.focusRow(rowIndex);
71136         }else{
71137             var isSelected = this.isSelected(rowIndex);
71138             if(e.ctrlKey && isSelected){
71139                 this.deselectRow(rowIndex);
71140             }else if(!isSelected || this.getCount() > 1){
71141                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
71142                 view.focusRow(rowIndex);
71143             }
71144         }
71145     },
71146
71147     /**
71148      * Selects multiple rows.
71149      * @param {Array} rows Array of the indexes of the row to select
71150      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
71151      * existing selections (defaults to <tt>false</tt>)
71152      */
71153     selectRows : function(rows, keepExisting){
71154         if(!keepExisting){
71155             this.clearSelections();
71156         }
71157         for(var i = 0, len = rows.length; i < len; i++){
71158             this.selectRow(rows[i], true);
71159         }
71160     },
71161
71162     /**
71163      * Selects a range of rows if the selection model
71164      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
71165      * All rows in between startRow and endRow are also selected.
71166      * @param {Number} startRow The index of the first row in the range
71167      * @param {Number} endRow The index of the last row in the range
71168      * @param {Boolean} keepExisting (optional) True to retain existing selections
71169      */
71170     selectRange : function(startRow, endRow, keepExisting){
71171         var i;
71172         if(this.isLocked()){
71173             return;
71174         }
71175         if(!keepExisting){
71176             this.clearSelections();
71177         }
71178         if(startRow <= endRow){
71179             for(i = startRow; i <= endRow; i++){
71180                 this.selectRow(i, true);
71181             }
71182         }else{
71183             for(i = startRow; i >= endRow; i--){
71184                 this.selectRow(i, true);
71185             }
71186         }
71187     },
71188
71189     /**
71190      * Deselects a range of rows if the selection model
71191      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
71192      * All rows in between startRow and endRow are also deselected.
71193      * @param {Number} startRow The index of the first row in the range
71194      * @param {Number} endRow The index of the last row in the range
71195      */
71196     deselectRange : function(startRow, endRow, preventViewNotify){
71197         if(this.isLocked()){
71198             return;
71199         }
71200         for(var i = startRow; i <= endRow; i++){
71201             this.deselectRow(i, preventViewNotify);
71202         }
71203     },
71204
71205     /**
71206      * Selects a row.  Before selecting a row, checks if the selection model
71207      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
71208      * {@link #beforerowselect} event.  If these checks are satisfied the row
71209      * will be selected and followed up by  firing the {@link #rowselect} and
71210      * {@link #selectionchange} events.
71211      * @param {Number} row The index of the row to select
71212      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
71213      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
71214      * prevent notifying the view (disables updating the selected appearance)
71215      */
71216     selectRow : function(index, keepExisting, preventViewNotify){
71217         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
71218             return;
71219         }
71220         var r = this.grid.store.getAt(index);
71221         if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
71222             if(!keepExisting || this.singleSelect){
71223                 this.clearSelections();
71224             }
71225             this.selections.add(r);
71226             this.last = this.lastActive = index;
71227             if(!preventViewNotify){
71228                 this.grid.getView().onRowSelect(index);
71229             }
71230             this.fireEvent('rowselect', this, index, r);
71231             this.fireEvent('selectionchange', this);
71232         }
71233     },
71234
71235     /**
71236      * Deselects a row.  Before deselecting a row, checks if the selection model
71237      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
71238      * If this check is satisfied the row will be deselected and followed up by
71239      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
71240      * @param {Number} row The index of the row to deselect
71241      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
71242      * prevent notifying the view (disables updating the selected appearance)
71243      */
71244     deselectRow : function(index, preventViewNotify){
71245         if(this.isLocked()){
71246             return;
71247         }
71248         if(this.last == index){
71249             this.last = false;
71250         }
71251         if(this.lastActive == index){
71252             this.lastActive = false;
71253         }
71254         var r = this.grid.store.getAt(index);
71255         if(r){
71256             this.selections.remove(r);
71257             if(!preventViewNotify){
71258                 this.grid.getView().onRowDeselect(index);
71259             }
71260             this.fireEvent('rowdeselect', this, index, r);
71261             this.fireEvent('selectionchange', this);
71262         }
71263     },
71264
71265     // private
71266     restoreLast : function(){
71267         if(this._last){
71268             this.last = this._last;
71269         }
71270     },
71271
71272     // private
71273     acceptsNav : function(row, col, cm){
71274         return !cm.isHidden(col) && cm.isCellEditable(col, row);
71275     },
71276
71277     // private
71278     onEditorKey : function(field, e){
71279         var k = e.getKey(), 
71280             newCell, 
71281             g = this.grid, 
71282             last = g.lastEdit,
71283             ed = g.activeEditor,
71284             ae, last, r, c;
71285         var shift = e.shiftKey;
71286         if(k == e.TAB){
71287             e.stopEvent();
71288             ed.completeEdit();
71289             if(shift){
71290                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
71291             }else{
71292                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
71293             }
71294         }else if(k == e.ENTER){
71295             if(this.moveEditorOnEnter !== false){
71296                 if(shift){
71297                     newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
71298                 }else{
71299                     newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
71300                 }
71301             }
71302         }
71303         if(newCell){
71304             r = newCell[0];
71305             c = newCell[1];
71306
71307             if(last.row != r){
71308                 this.selectRow(r); // *** highlight newly-selected cell and update selection
71309             }
71310
71311             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
71312                 ae = g.activeEditor;
71313                 if(ae && ae.field.triggerBlur){
71314                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
71315                     ae.field.triggerBlur();
71316                 }
71317             }
71318             g.startEditing(r, c);
71319         }
71320     },
71321     
71322     destroy : function(){
71323         if(this.rowNav){
71324             this.rowNav.disable();
71325             this.rowNav = null;
71326         }
71327         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
71328     }
71329 });/**
71330  * @class Ext.grid.Column
71331  * <p>This class encapsulates column configuration data to be used in the initialization of a
71332  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
71333  * <p>While subclasses are provided to render data in different ways, this class renders a passed
71334  * data field unchanged and is usually used for textual columns.</p>
71335  */
71336 Ext.grid.Column = Ext.extend(Object, {
71337     /**
71338      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured
71339      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.
71340      * The initial configuration may be dynamically altered using
71341      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.
71342      */
71343     /**
71344      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial
71345      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all
71346      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be
71347      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>
71348      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>
71349      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>
71350      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this
71351      * unique identifier.
71352      */
71353     /**
71354      * @cfg {String} header Optional. The header text to be used as innerHTML
71355      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to
71356      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
71357      */
71358     /**
71359      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71360      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,
71361      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the
71362      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.
71363      */
71364     /**
71365      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71366      * may be used to specify the text with which to prefix the group field value in the group header line.
71367      * See also {@link #groupRenderer} and
71368      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.
71369      */
71370     /**
71371      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71372      * may be used to specify the function used to format the grouping field value for display in the group
71373      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured
71374      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified
71375      * the new value of the group field will be used.</p>
71376      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be
71377      * passed the following parameters:
71378      * <div class="mdetail-params"><ul>
71379      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>
71380      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>
71381      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data
71382      * for the row which caused group change.</p></li>
71383      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
71384      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>
71385      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
71386      * </ul></div></p>
71387      * <p>The function should return a string value.</p>
71388      */
71389     /**
71390      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71391      * may be used to specify the text to display when there is an empty group value. Defaults to the
71392      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.
71393      */
71394     /**
71395      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
71396      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
71397      * which to draw the column's value.</p>
71398      */
71399     /**
71400      * @cfg {Number} width
71401      * Optional. The initial width in pixels of the column.
71402      * The width of each column can also be affected if any of the following are configured:
71403      * <div class="mdetail-params"><ul>
71404      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>
71405      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>
71406      * <div class="sub-desc">
71407      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be
71408      * re-proportioned (based on the relative initial widths) to fill the width of the grid so
71409      * that no horizontal scrollbar is shown.</p>
71410      * </div></li>
71411      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>
71412      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>
71413      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side
71414      * is reserved for the vertical scrollbar.  The
71415      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>
71416      * can be modified to reduce or eliminate the reserved offset.</p>
71417      */
71418     /**
71419      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
71420      * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.
71421      * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.
71422      */
71423     /**
71424      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.
71425      */
71426     /**
71427      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.
71428      */
71429     /**
71430      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.
71431      */
71432     /**
71433      * @cfg {Boolean} hidden
71434      * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.
71435      * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.
71436      * If a column is never to be shown, simply do not include this column in the Column Model at all.
71437      */
71438     /**
71439      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips
71440      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the
71441      * header's HTML title attribute. Defaults to ''.
71442      */
71443     /**
71444      * @cfg {Mixed} renderer
71445      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>
71446      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,
71447      * appearance, etc.) before it is rendered). This may be specified in either of three ways:
71448      * <div class="mdetail-params"><ul>
71449      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>
71450      * <li>A string which references a property name of the {@link Ext.util.Format} class which
71451      * provides a renderer function.</li>
71452      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>
71453      * reference) e.g.:<pre style="margin-left:1.2em"><code>
71454 {
71455     fn: this.gridRenderer,
71456     scope: this
71457 }
71458 </code></pre></li></ul></div>
71459      * If not specified, the default renderer uses the raw data value.</p>
71460      * <p>For information about the renderer function (passed parameters, etc.), see
71461      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>
71462 var companyColumn = {
71463    header: 'Company Name',
71464    dataIndex: 'company',
71465    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
71466       // provide the logic depending on business rules
71467       // name of your own choosing to manipulate the cell depending upon
71468       // the data in the underlying Record object.
71469       if (value == 'whatever') {
71470           //metaData.css : String : A CSS class name to add to the TD element of the cell.
71471           //metaData.attr : String : An html attribute definition string to apply to
71472           //                         the data container element within the table
71473           //                         cell (e.g. 'style="color:red;"').
71474           metaData.css = 'name-of-css-class-you-will-define';
71475       }
71476       return value;
71477    }
71478 }
71479      * </code></pre>
71480      * See also {@link #scope}.
71481      */
71482     /**
71483      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass
71484      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily
71485      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:
71486      * <div class="mdetail-params"><ul>
71487      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>
71488      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>
71489      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>
71490      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>
71491      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>
71492      * </ul></div>
71493      * <p>Configuration properties for the specified <code>xtype</code> may be specified with
71494      * the Column configuration properties, for example:</p>
71495      * <pre><code>
71496 var grid = new Ext.grid.GridPanel({
71497     ...
71498     columns: [{
71499         header: 'Last Updated',
71500         dataIndex: 'lastChange',
71501         width: 85,
71502         sortable: true,
71503         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),
71504         xtype: 'datecolumn', // use xtype instead of renderer
71505         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}
71506     }, {
71507         ...
71508     }]
71509 });
71510      * </code></pre>
71511      */
71512     /**
71513      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the
71514      * renderer.  Defaults to the Column configuration object.
71515      */
71516     /**
71517      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.
71518      */
71519     /**
71520      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column
71521      * (excluding headers). Defaults to undefined.
71522      */
71523     /**
71524      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
71525      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use
71526      * {@link Ext.grid.GridPanel#enableColumnHide} instead.
71527      */
71528     /**
71529      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column
71530      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.
71531      */
71532
71533     /**
71534      * @private
71535      * @cfg {Boolean} isColumn
71536      * Used by ColumnModel setConfig method to avoid reprocessing a Column
71537      * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column
71538      * Defaults to true.
71539      */
71540     isColumn : true,
71541
71542     constructor : function(config){
71543         Ext.apply(this, config);
71544
71545         if(Ext.isString(this.renderer)){
71546             this.renderer = Ext.util.Format[this.renderer];
71547         }else if(Ext.isObject(this.renderer)){
71548             this.scope = this.renderer.scope;
71549             this.renderer = this.renderer.fn;
71550         }
71551         if(!this.scope){
71552             this.scope = this;
71553         }
71554
71555         var ed = this.editor;
71556         delete this.editor;
71557         this.setEditor(ed);
71558     },
71559
71560     /**
71561      * Optional. A function which returns displayable data when passed the following parameters:
71562      * <div class="mdetail-params"><ul>
71563      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
71564      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
71565      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
71566      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container
71567      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>
71568      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was
71569      * extracted.</p></li>
71570      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
71571      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
71572      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record
71573      * was extracted.</p></li>
71574      * </ul></div>
71575      * @property renderer
71576      * @type Function
71577      */
71578     renderer : function(value){
71579         if(Ext.isString(value) && value.length < 1){
71580             return '&#160;';
71581         }
71582         return value;
71583     },
71584
71585     // private
71586     getEditor: function(rowIndex){
71587         return this.editable !== false ? this.editor : null;
71588     },
71589
71590     /**
71591      * Sets a new editor for this column.
71592      * @param {Ext.Editor/Ext.form.Field} editor The editor to set
71593      */
71594     setEditor : function(editor){
71595         var ed = this.editor;
71596         if(ed){
71597             if(ed.gridEditor){
71598                 ed.gridEditor.destroy();
71599                 delete ed.gridEditor;
71600             }else{
71601                 ed.destroy();
71602             }
71603         }
71604         this.editor = null;
71605         if(editor){
71606             //not an instance, create it
71607             if(!editor.isXType){
71608                 editor = Ext.create(editor, 'textfield');
71609             }
71610             this.editor = editor;
71611         }
71612     },
71613
71614     /**
71615      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}
71616      * used to edit the cell.
71617      * @param {Number} rowIndex The row index
71618      * @return {Ext.Editor}
71619      */
71620     getCellEditor: function(rowIndex){
71621         var ed = this.getEditor(rowIndex);
71622         if(ed){
71623             if(!ed.startEdit){
71624                 if(!ed.gridEditor){
71625                     ed.gridEditor = new Ext.grid.GridEditor(ed);
71626                 }
71627                 ed = ed.gridEditor;
71628             }
71629         }
71630         return ed;
71631     }
71632 });
71633
71634 /**
71635  * @class Ext.grid.BooleanColumn
71636  * @extends Ext.grid.Column
71637  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.Column#xtype xtype}
71638  * config option of {@link Ext.grid.Column} for more details.</p>
71639  */
71640 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {
71641     /**
71642      * @cfg {String} trueText
71643      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
71644      */
71645     trueText: 'true',
71646     /**
71647      * @cfg {String} falseText
71648      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
71649      * <tt>'false'</tt>).
71650      */
71651     falseText: 'false',
71652     /**
71653      * @cfg {String} undefinedText
71654      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
71655      */
71656     undefinedText: '&#160;',
71657
71658     constructor: function(cfg){
71659         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);
71660         var t = this.trueText, f = this.falseText, u = this.undefinedText;
71661         this.renderer = function(v){
71662             if(v === undefined){
71663                 return u;
71664             }
71665             if(!v || v === 'false'){
71666                 return f;
71667             }
71668             return t;
71669         };
71670     }
71671 });
71672
71673 /**
71674  * @class Ext.grid.NumberColumn
71675  * @extends Ext.grid.Column
71676  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
71677  * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>
71678  */
71679 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {
71680     /**
71681      * @cfg {String} format
71682      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
71683      * (defaults to <tt>'0,000.00'</tt>).
71684      */
71685     format : '0,000.00',
71686     constructor: function(cfg){
71687         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);
71688         this.renderer = Ext.util.Format.numberRenderer(this.format);
71689     }
71690 });
71691
71692 /**
71693  * @class Ext.grid.DateColumn
71694  * @extends Ext.grid.Column
71695  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
71696  * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}
71697  * for more details.</p>
71698  */
71699 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {
71700     /**
71701      * @cfg {String} format
71702      * A formatting string as used by {@link Date#format} to format a Date for this Column
71703      * (defaults to <tt>'m/d/Y'</tt>).
71704      */
71705     format : 'm/d/Y',
71706     constructor: function(cfg){
71707         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);
71708         this.renderer = Ext.util.Format.dateRenderer(this.format);
71709     }
71710 });
71711
71712 /**
71713  * @class Ext.grid.TemplateColumn
71714  * @extends Ext.grid.Column
71715  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s
71716  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
71717  * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more
71718  * details.</p>
71719  */
71720 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {
71721     /**
71722      * @cfg {String/XTemplate} tpl
71723      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
71724      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.
71725      */
71726     constructor: function(cfg){
71727         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);
71728         var tpl = (!Ext.isPrimitive(this.tpl) && this.tpl.compile) ? this.tpl : new Ext.XTemplate(this.tpl);
71729         this.renderer = function(value, p, r){
71730             return tpl.apply(r.data);
71731         };
71732         this.tpl = tpl;
71733     }
71734 });
71735
71736 /*
71737  * @property types
71738  * @type Object
71739  * @member Ext.grid.Column
71740  * @static
71741  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced
71742  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>
71743  * <p>This contains the following properties</p><div class="mdesc-details"><ul>
71744  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>
71745  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>
71746  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>
71747  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>
71748  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>
71749  * </ul></div>
71750  */
71751 Ext.grid.Column.types = {
71752     gridcolumn : Ext.grid.Column,
71753     booleancolumn: Ext.grid.BooleanColumn,
71754     numbercolumn: Ext.grid.NumberColumn,
71755     datecolumn: Ext.grid.DateColumn,
71756     templatecolumn: Ext.grid.TemplateColumn
71757 };/**
71758  * @class Ext.grid.RowNumberer
71759  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
71760  * an automatic row numbering column.
71761  * <br>Usage:<br>
71762  <pre><code>
71763  // This is a typical column config with the first column providing row numbers
71764  var colModel = new Ext.grid.ColumnModel([
71765     new Ext.grid.RowNumberer(),
71766     {header: "Name", width: 80, sortable: true},
71767     {header: "Code", width: 50, sortable: true},
71768     {header: "Description", width: 200, sortable: true}
71769  ]);
71770  </code></pre>
71771  * @constructor
71772  * @param {Object} config The configuration options
71773  */
71774 Ext.grid.RowNumberer = Ext.extend(Object, {
71775     /**
71776      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
71777      * number column (defaults to '').
71778      */
71779     header: "",
71780     /**
71781      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
71782      */
71783     width: 23,
71784     /**
71785      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
71786      * @hide
71787      */
71788     sortable: false,
71789     
71790     constructor : function(config){
71791         Ext.apply(this, config);
71792         if(this.rowspan){
71793             this.renderer = this.renderer.createDelegate(this);
71794         }
71795     },
71796
71797     // private
71798     fixed:true,
71799     hideable: false,
71800     menuDisabled:true,
71801     dataIndex: '',
71802     id: 'numberer',
71803     rowspan: undefined,
71804
71805     // private
71806     renderer : function(v, p, record, rowIndex){
71807         if(this.rowspan){
71808             p.cellAttr = 'rowspan="'+this.rowspan+'"';
71809         }
71810         return rowIndex+1;
71811     }
71812 });/**
71813  * @class Ext.grid.CheckboxSelectionModel
71814  * @extends Ext.grid.RowSelectionModel
71815  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
71816  * @constructor
71817  * @param {Object} config The configuration options
71818  */
71819 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
71820
71821     /**
71822      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
71823      * checkbox column (defaults to <tt>false</tt>).
71824      */
71825     /**
71826      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the
71827      * checkbox column.  Defaults to:<pre><code>
71828      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>
71829      * </code></pre>
71830      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header
71831      * and provides support for automatic check all/none behavior on header click. This string
71832      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,
71833      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the
71834      * <tt>'x-grid3-hd-checker'</tt> class is supplied.
71835      */
71836     header : '<div class="x-grid3-hd-checker">&#160;</div>',
71837     /**
71838      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).
71839      */
71840     width : 20,
71841     /**
71842      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to
71843      * <tt>false</tt>).
71844      */
71845     sortable : false,
71846
71847     // private
71848     menuDisabled : true,
71849     fixed : true,
71850     hideable: false,
71851     dataIndex : '',
71852     id : 'checker',
71853
71854     constructor : function(){
71855         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);
71856
71857         if(this.checkOnly){
71858             this.handleMouseDown = Ext.emptyFn;
71859         }
71860     },
71861
71862     // private
71863     initEvents : function(){
71864         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
71865         this.grid.on('render', function(){
71866             var view = this.grid.getView();
71867             view.mainBody.on('mousedown', this.onMouseDown, this);
71868             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
71869
71870         }, this);
71871     },
71872
71873     // If handleMouseDown was called from another event (enableDragDrop), set a flag so
71874     // onMouseDown does not process it a second time
71875     handleMouseDown : function() {
71876         Ext.grid.CheckboxSelectionModel.superclass.handleMouseDown.apply(this, arguments);
71877         this.mouseHandled = true;
71878     },
71879
71880     // private
71881     onMouseDown : function(e, t){
71882         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
71883             e.stopEvent();
71884             var row = e.getTarget('.x-grid3-row');
71885
71886             // mouseHandled flag check for a duplicate selection (handleMouseDown) call
71887             if(!this.mouseHandled && row){
71888                 var index = row.rowIndex;
71889                 if(this.isSelected(index)){
71890                     this.deselectRow(index);
71891                 }else{
71892                     this.selectRow(index, true);
71893                 }
71894             }
71895         }
71896         this.mouseHandled = false;
71897     },
71898
71899     // private
71900     onHdMouseDown : function(e, t){
71901         if(t.className == 'x-grid3-hd-checker'){
71902             e.stopEvent();
71903             var hd = Ext.fly(t.parentNode);
71904             var isChecked = hd.hasClass('x-grid3-hd-checker-on');
71905             if(isChecked){
71906                 hd.removeClass('x-grid3-hd-checker-on');
71907                 this.clearSelections();
71908             }else{
71909                 hd.addClass('x-grid3-hd-checker-on');
71910                 this.selectAll();
71911             }
71912         }
71913     },
71914
71915     // private
71916     renderer : function(v, p, record){
71917         return '<div class="x-grid3-row-checker">&#160;</div>';
71918     }
71919 });/**
71920  * @class Ext.grid.CellSelectionModel
71921  * @extends Ext.grid.AbstractSelectionModel
71922  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
71923  * The object stored as the selection contains the following properties:
71924  * <div class="mdetail-params"><ul>
71925  * <li><b>cell</b> : see {@link #getSelectedCell} 
71926  * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
71927  * which provides the data for the row containing the selection</li>
71928  * </ul></div>
71929  * @constructor
71930  * @param {Object} config The object containing the configuration of this model.
71931  */
71932 Ext.grid.CellSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
71933     
71934     constructor : function(config){
71935         Ext.apply(this, config);
71936
71937             this.selection = null;
71938         
71939             this.addEvents(
71940                 /**
71941                  * @event beforecellselect
71942                  * Fires before a cell is selected, return false to cancel the selection.
71943                  * @param {SelectionModel} this
71944                  * @param {Number} rowIndex The selected row index
71945                  * @param {Number} colIndex The selected cell index
71946                  */
71947                 "beforecellselect",
71948                 /**
71949                  * @event cellselect
71950                  * Fires when a cell is selected.
71951                  * @param {SelectionModel} this
71952                  * @param {Number} rowIndex The selected row index
71953                  * @param {Number} colIndex The selected cell index
71954                  */
71955                 "cellselect",
71956                 /**
71957                  * @event selectionchange
71958                  * Fires when the active selection changes.
71959                  * @param {SelectionModel} this
71960                  * @param {Object} selection null for no selection or an object with two properties
71961                  * <div class="mdetail-params"><ul>
71962                  * <li><b>cell</b> : see {@link #getSelectedCell} 
71963                  * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
71964                  * which provides the data for the row containing the selection</p></li>
71965                  * </ul></div>
71966                  */
71967                 "selectionchange"
71968             );
71969         
71970             Ext.grid.CellSelectionModel.superclass.constructor.call(this);
71971     },
71972
71973     /** @ignore */
71974     initEvents : function(){
71975         this.grid.on('cellmousedown', this.handleMouseDown, this);
71976         this.grid.on(Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.handleKeyDown, this);
71977         this.grid.getView().on({
71978             scope: this,
71979             refresh: this.onViewChange,
71980             rowupdated: this.onRowUpdated,
71981             beforerowremoved: this.clearSelections,
71982             beforerowsinserted: this.clearSelections
71983         });
71984         if(this.grid.isEditor){
71985             this.grid.on('beforeedit', this.beforeEdit,  this);
71986         }
71987     },
71988
71989         //private
71990     beforeEdit : function(e){
71991         this.select(e.row, e.column, false, true, e.record);
71992     },
71993
71994         //private
71995     onRowUpdated : function(v, index, r){
71996         if(this.selection && this.selection.record == r){
71997             v.onCellSelect(index, this.selection.cell[1]);
71998         }
71999     },
72000
72001         //private
72002     onViewChange : function(){
72003         this.clearSelections(true);
72004     },
72005
72006         /**
72007      * Returns an array containing the row and column indexes of the currently selected cell
72008      * (e.g., [0, 0]), or null if none selected. The array has elements:
72009      * <div class="mdetail-params"><ul>
72010      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
72011      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell. 
72012      * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
72013      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
72014      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
72015 // get name
72016 var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
72017 // get data value based on name
72018 var data = record.get(fieldName);
72019      * </code></pre></p></li>
72020      * </ul></div>
72021      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
72022          */
72023     getSelectedCell : function(){
72024         return this.selection ? this.selection.cell : null;
72025     },
72026
72027     /**
72028      * If anything is selected, clears all selections and fires the selectionchange event.
72029      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
72030      * being notified about the change.
72031      */
72032     clearSelections : function(preventNotify){
72033         var s = this.selection;
72034         if(s){
72035             if(preventNotify !== true){
72036                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
72037             }
72038             this.selection = null;
72039             this.fireEvent("selectionchange", this, null);
72040         }
72041     },
72042
72043     /**
72044      * Returns <tt>true</tt> if there is a selection.
72045      * @return {Boolean}
72046      */
72047     hasSelection : function(){
72048         return this.selection ? true : false;
72049     },
72050
72051     /** @ignore */
72052     handleMouseDown : function(g, row, cell, e){
72053         if(e.button !== 0 || this.isLocked()){
72054             return;
72055         }
72056         this.select(row, cell);
72057     },
72058
72059     /**
72060      * Selects a cell.  Before selecting a cell, fires the
72061      * {@link #beforecellselect} event.  If this check is satisfied the cell
72062      * will be selected and followed up by  firing the {@link #cellselect} and
72063      * {@link #selectionchange} events.
72064      * @param {Number} rowIndex The index of the row to select
72065      * @param {Number} colIndex The index of the column to select
72066      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
72067      * prevent notifying the view (disables updating the selected appearance)
72068      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
72069      * the specified rowIndex / colIndex from being focused.
72070      * @param {Ext.data.Record} r (optional) The record to select
72071      */
72072     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
72073         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
72074             this.clearSelections();
72075             r = r || this.grid.store.getAt(rowIndex);
72076             this.selection = {
72077                 record : r,
72078                 cell : [rowIndex, colIndex]
72079             };
72080             if(!preventViewNotify){
72081                 var v = this.grid.getView();
72082                 v.onCellSelect(rowIndex, colIndex);
72083                 if(preventFocus !== true){
72084                     v.focusCell(rowIndex, colIndex);
72085                 }
72086             }
72087             this.fireEvent("cellselect", this, rowIndex, colIndex);
72088             this.fireEvent("selectionchange", this, this.selection);
72089         }
72090     },
72091
72092         //private
72093     isSelectable : function(rowIndex, colIndex, cm){
72094         return !cm.isHidden(colIndex);
72095     },
72096     
72097     // private
72098     onEditorKey: function(field, e){
72099         if(e.getKey() == e.TAB){
72100             this.handleKeyDown(e);
72101         }
72102     },
72103
72104     /** @ignore */
72105     handleKeyDown : function(e){
72106         if(!e.isNavKeyPress()){
72107             return;
72108         }
72109         
72110         var k = e.getKey(),
72111             g = this.grid,
72112             s = this.selection,
72113             sm = this,
72114             walk = function(row, col, step){
72115                 return g.walkCells(
72116                     row,
72117                     col,
72118                     step,
72119                     g.isEditor && g.editing ? sm.acceptsNav : sm.isSelectable, // *** handle tabbing while editorgrid is in edit mode
72120                     sm
72121                 );
72122             },
72123             cell, newCell, r, c, ae;
72124
72125         switch(k){
72126             case e.ESC:
72127             case e.PAGE_UP:
72128             case e.PAGE_DOWN:
72129                 // do nothing
72130                 break;
72131             default:
72132                 // *** call e.stopEvent() only for non ESC, PAGE UP/DOWN KEYS
72133                 e.stopEvent();
72134                 break;
72135         }
72136
72137         if(!s){
72138             cell = walk(0, 0, 1); // *** use private walk() function defined above
72139             if(cell){
72140                 this.select(cell[0], cell[1]);
72141             }
72142             return;
72143         }
72144
72145         cell = s.cell;  // currently selected cell
72146         r = cell[0];    // current row
72147         c = cell[1];    // current column
72148         
72149         switch(k){
72150             case e.TAB:
72151                 if(e.shiftKey){
72152                     newCell = walk(r, c - 1, -1);
72153                 }else{
72154                     newCell = walk(r, c + 1, 1);
72155                 }
72156                 break;
72157             case e.DOWN:
72158                 newCell = walk(r + 1, c, 1);
72159                 break;
72160             case e.UP:
72161                 newCell = walk(r - 1, c, -1);
72162                 break;
72163             case e.RIGHT:
72164                 newCell = walk(r, c + 1, 1);
72165                 break;
72166             case e.LEFT:
72167                 newCell = walk(r, c - 1, -1);
72168                 break;
72169             case e.ENTER:
72170                 if (g.isEditor && !g.editing) {
72171                     g.startEditing(r, c);
72172                     return;
72173                 }
72174                 break;
72175         }
72176
72177         if(newCell){
72178             // *** reassign r & c variables to newly-selected cell's row and column
72179             r = newCell[0];
72180             c = newCell[1];
72181
72182             this.select(r, c); // *** highlight newly-selected cell and update selection
72183
72184             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
72185                 ae = g.activeEditor;
72186                 if(ae && ae.field.triggerBlur){
72187                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
72188                     ae.field.triggerBlur();
72189                 }
72190                 g.startEditing(r, c);
72191             }
72192         }
72193     },
72194
72195     acceptsNav : function(row, col, cm){
72196         return !cm.isHidden(col) && cm.isCellEditable(col, row);
72197     }
72198 });/**
72199  * @class Ext.grid.EditorGridPanel
72200  * @extends Ext.grid.GridPanel
72201  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
72202  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
72203  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
72204  * <p>Editability of columns may be controlled programatically by inserting an implementation
72205  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
72206  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
72207  * <p>Editing is performed on the value of the <i>field</i> specified by the column's
72208  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
72209  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
72210  * transformed data, this must be accounted for).</p>
72211  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
72212  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
72213  * mapping would be an appropriate editor.</p>
72214  * If there is a more complex mismatch between the visible data in the grid, and the editable data in
72215  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
72216  * injected using the {@link #beforeedit} and {@link #afteredit} events.
72217  * @constructor
72218  * @param {Object} config The config object
72219  * @xtype editorgrid
72220  */
72221 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
72222     /**
72223      * @cfg {Number} clicksToEdit
72224      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
72225      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
72226      * editing that cell.</p>
72227      */
72228     clicksToEdit: 2,
72229
72230     /**
72231     * @cfg {Boolean} forceValidation
72232     * True to force validation even if the value is unmodified (defaults to false)
72233     */
72234     forceValidation: false,
72235
72236     // private
72237     isEditor : true,
72238     // private
72239     detectEdit: false,
72240
72241     /**
72242      * @cfg {Boolean} autoEncode
72243      * True to automatically HTML encode and decode values pre and post edit (defaults to false)
72244      */
72245     autoEncode : false,
72246
72247     /**
72248      * @cfg {Boolean} trackMouseOver @hide
72249      */
72250     // private
72251     trackMouseOver: false, // causes very odd FF errors
72252
72253     // private
72254     initComponent : function(){
72255         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
72256
72257         if(!this.selModel){
72258             /**
72259              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
72260              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
72261              */
72262             this.selModel = new Ext.grid.CellSelectionModel();
72263         }
72264
72265         this.activeEditor = null;
72266
72267         this.addEvents(
72268             /**
72269              * @event beforeedit
72270              * Fires before cell editing is triggered. The edit event object has the following properties <br />
72271              * <ul style="padding:5px;padding-left:16px;">
72272              * <li>grid - This grid</li>
72273              * <li>record - The record being edited</li>
72274              * <li>field - The field name being edited</li>
72275              * <li>value - The value for the field being edited.</li>
72276              * <li>row - The grid row index</li>
72277              * <li>column - The grid column index</li>
72278              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
72279              * </ul>
72280              * @param {Object} e An edit event (see above for description)
72281              */
72282             "beforeedit",
72283             /**
72284              * @event afteredit
72285              * Fires after a cell is edited. The edit event object has the following properties <br />
72286              * <ul style="padding:5px;padding-left:16px;">
72287              * <li>grid - This grid</li>
72288              * <li>record - The record being edited</li>
72289              * <li>field - The field name being edited</li>
72290              * <li>value - The value being set</li>
72291              * <li>originalValue - The original value for the field, before the edit.</li>
72292              * <li>row - The grid row index</li>
72293              * <li>column - The grid column index</li>
72294              * </ul>
72295              *
72296              * <pre><code>
72297 grid.on('afteredit', afterEdit, this );
72298
72299 function afterEdit(e) {
72300     // execute an XHR to send/commit data to the server, in callback do (if successful):
72301     e.record.commit();
72302 };
72303              * </code></pre>
72304              * @param {Object} e An edit event (see above for description)
72305              */
72306             "afteredit",
72307             /**
72308              * @event validateedit
72309              * Fires after a cell is edited, but before the value is set in the record. Return false
72310              * to cancel the change. The edit event object has the following properties <br />
72311              * <ul style="padding:5px;padding-left:16px;">
72312              * <li>grid - This grid</li>
72313              * <li>record - The record being edited</li>
72314              * <li>field - The field name being edited</li>
72315              * <li>value - The value being set</li>
72316              * <li>originalValue - The original value for the field, before the edit.</li>
72317              * <li>row - The grid row index</li>
72318              * <li>column - The grid column index</li>
72319              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
72320              * </ul>
72321              * Usage example showing how to remove the red triangle (dirty record indicator) from some
72322              * records (not all).  By observing the grid's validateedit event, it can be cancelled if
72323              * the edit occurs on a targeted row (for example) and then setting the field's new value
72324              * in the Record directly:
72325              * <pre><code>
72326 grid.on('validateedit', function(e) {
72327   var myTargetRow = 6;
72328
72329   if (e.row == myTargetRow) {
72330     e.cancel = true;
72331     e.record.data[e.field] = e.value;
72332   }
72333 });
72334              * </code></pre>
72335              * @param {Object} e An edit event (see above for description)
72336              */
72337             "validateedit"
72338         );
72339     },
72340
72341     // private
72342     initEvents : function(){
72343         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
72344
72345         this.getGridEl().on('mousewheel', this.stopEditing.createDelegate(this, [true]), this);
72346         this.on('columnresize', this.stopEditing, this, [true]);
72347
72348         if(this.clicksToEdit == 1){
72349             this.on("cellclick", this.onCellDblClick, this);
72350         }else {
72351             var view = this.getView();
72352             if(this.clicksToEdit == 'auto' && view.mainBody){
72353                 view.mainBody.on('mousedown', this.onAutoEditClick, this);
72354             }
72355             this.on('celldblclick', this.onCellDblClick, this);
72356         }
72357     },
72358
72359     onResize : function(){
72360         Ext.grid.EditorGridPanel.superclass.onResize.apply(this, arguments);
72361         var ae = this.activeEditor;
72362         if(this.editing && ae){
72363             ae.realign(true);
72364         }
72365     },
72366
72367     // private
72368     onCellDblClick : function(g, row, col){
72369         this.startEditing(row, col);
72370     },
72371
72372     // private
72373     onAutoEditClick : function(e, t){
72374         if(e.button !== 0){
72375             return;
72376         }
72377         var row = this.view.findRowIndex(t),
72378             col = this.view.findCellIndex(t);
72379         if(row !== false && col !== false){
72380             this.stopEditing();
72381             if(this.selModel.getSelectedCell){ // cell sm
72382                 var sc = this.selModel.getSelectedCell();
72383                 if(sc && sc[0] === row && sc[1] === col){
72384                     this.startEditing(row, col);
72385                 }
72386             }else{
72387                 if(this.selModel.isSelected(row)){
72388                     this.startEditing(row, col);
72389                 }
72390             }
72391         }
72392     },
72393
72394     // private
72395     onEditComplete : function(ed, value, startValue){
72396         this.editing = false;
72397         this.lastActiveEditor = this.activeEditor;
72398         this.activeEditor = null;
72399
72400         var r = ed.record,
72401             field = this.colModel.getDataIndex(ed.col);
72402         value = this.postEditValue(value, startValue, r, field);
72403         if(this.forceValidation === true || String(value) !== String(startValue)){
72404             var e = {
72405                 grid: this,
72406                 record: r,
72407                 field: field,
72408                 originalValue: startValue,
72409                 value: value,
72410                 row: ed.row,
72411                 column: ed.col,
72412                 cancel:false
72413             };
72414             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
72415                 r.set(field, e.value);
72416                 delete e.cancel;
72417                 this.fireEvent("afteredit", e);
72418             }
72419         }
72420         this.view.focusCell(ed.row, ed.col);
72421     },
72422
72423     /**
72424      * Starts editing the specified for the specified row/column
72425      * @param {Number} rowIndex
72426      * @param {Number} colIndex
72427      */
72428     startEditing : function(row, col){
72429         this.stopEditing();
72430         if(this.colModel.isCellEditable(col, row)){
72431             this.view.ensureVisible(row, col, true);
72432             var r = this.store.getAt(row),
72433                 field = this.colModel.getDataIndex(col),
72434                 e = {
72435                     grid: this,
72436                     record: r,
72437                     field: field,
72438                     value: r.data[field],
72439                     row: row,
72440                     column: col,
72441                     cancel:false
72442                 };
72443             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
72444                 this.editing = true;
72445                 var ed = this.colModel.getCellEditor(col, row);
72446                 if(!ed){
72447                     return;
72448                 }
72449                 if(!ed.rendered){
72450                     ed.parentEl = this.view.getEditorParent(ed);
72451                     ed.on({
72452                         scope: this,
72453                         render: {
72454                             fn: function(c){
72455                                 c.field.focus(false, true);
72456                             },
72457                             single: true,
72458                             scope: this
72459                         },
72460                         specialkey: function(field, e){
72461                             this.getSelectionModel().onEditorKey(field, e);
72462                         },
72463                         complete: this.onEditComplete,
72464                         canceledit: this.stopEditing.createDelegate(this, [true])
72465                     });
72466                 }
72467                 Ext.apply(ed, {
72468                     row     : row,
72469                     col     : col,
72470                     record  : r
72471                 });
72472                 this.lastEdit = {
72473                     row: row,
72474                     col: col
72475                 };
72476                 this.activeEditor = ed;
72477                 // Set the selectSameEditor flag if we are reusing the same editor again and
72478                 // need to prevent the editor from firing onBlur on itself.
72479                 ed.selectSameEditor = (this.activeEditor == this.lastActiveEditor);
72480                 var v = this.preEditValue(r, field);
72481                 ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
72482
72483                 // Clear the selectSameEditor flag
72484                 (function(){
72485                     delete ed.selectSameEditor;
72486                 }).defer(50);
72487             }
72488         }
72489     },
72490
72491     // private
72492     preEditValue : function(r, field){
72493         var value = r.data[field];
72494         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlDecode(value) : value;
72495     },
72496
72497     // private
72498     postEditValue : function(value, originalValue, r, field){
72499         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlEncode(value) : value;
72500     },
72501
72502     /**
72503      * Stops any active editing
72504      * @param {Boolean} cancel (optional) True to cancel any changes
72505      */
72506     stopEditing : function(cancel){
72507         if(this.editing){
72508             // Store the lastActiveEditor to check if it is changing
72509             var ae = this.lastActiveEditor = this.activeEditor;
72510             if(ae){
72511                 ae[cancel === true ? 'cancelEdit' : 'completeEdit']();
72512                 this.view.focusCell(ae.row, ae.col);
72513             }
72514             this.activeEditor = null;
72515         }
72516         this.editing = false;
72517     }
72518 });
72519 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
72520 // This is a support class used internally by the Grid components
72521 Ext.grid.GridEditor = function(field, config){
72522     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
72523     field.monitorTab = false;
72524 };
72525
72526 Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
72527     alignment: "tl-tl",
72528     autoSize: "width",
72529     hideEl : false,
72530     cls: "x-small-editor x-grid-editor",
72531     shim:false,
72532     shadow:false
72533 });/**
72534  * @class Ext.grid.PropertyRecord
72535  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the
72536  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be
72537  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}
72538  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records
72539  * can also be created explicitly as shwon below.  Example usage:
72540  * <pre><code>
72541 var rec = new Ext.grid.PropertyRecord({
72542     name: 'Birthday',
72543     value: new Date(Date.parse('05/26/1972'))
72544 });
72545 // Add record to an already populated grid
72546 grid.store.addSorted(rec);
72547 </code></pre>
72548  * @constructor
72549  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type
72550  * will be read automatically by the grid to determine the type of editor to use when displaying it.
72551  */
72552 Ext.grid.PropertyRecord = Ext.data.Record.create([
72553     {name:'name',type:'string'}, 'value'
72554 ]);
72555
72556 /**
72557  * @class Ext.grid.PropertyStore
72558  * @extends Ext.util.Observable
72559  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping
72560  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format
72561  * required for compatibility with the underlying store. Generally this class should not need to be used directly --
72562  * the grid's data should be accessed from the underlying store via the {@link #store} property.
72563  * @constructor
72564  * @param {Ext.grid.Grid} grid The grid this store will be bound to
72565  * @param {Object} source The source data config object
72566  */
72567 Ext.grid.PropertyStore = Ext.extend(Ext.util.Observable, {
72568     
72569     constructor : function(grid, source){
72570         this.grid = grid;
72571         this.store = new Ext.data.Store({
72572             recordType : Ext.grid.PropertyRecord
72573         });
72574         this.store.on('update', this.onUpdate,  this);
72575         if(source){
72576             this.setSource(source);
72577         }
72578         Ext.grid.PropertyStore.superclass.constructor.call(this);    
72579     },
72580     
72581     // protected - should only be called by the grid.  Use grid.setSource instead.
72582     setSource : function(o){
72583         this.source = o;
72584         this.store.removeAll();
72585         var data = [];
72586         for(var k in o){
72587             if(this.isEditableValue(o[k])){
72588                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));
72589             }
72590         }
72591         this.store.loadRecords({records: data}, {}, true);
72592     },
72593
72594     // private
72595     onUpdate : function(ds, record, type){
72596         if(type == Ext.data.Record.EDIT){
72597             var v = record.data.value;
72598             var oldValue = record.modified.value;
72599             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
72600                 this.source[record.id] = v;
72601                 record.commit();
72602                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
72603             }else{
72604                 record.reject();
72605             }
72606         }
72607     },
72608
72609     // private
72610     getProperty : function(row){
72611        return this.store.getAt(row);
72612     },
72613
72614     // private
72615     isEditableValue: function(val){
72616         return Ext.isPrimitive(val) || Ext.isDate(val);
72617     },
72618
72619     // private
72620     setValue : function(prop, value, create){
72621         var r = this.getRec(prop);
72622         if(r){
72623             r.set('value', value);
72624             this.source[prop] = value;
72625         }else if(create){
72626             // only create if specified.
72627             this.source[prop] = value;
72628             r = new Ext.grid.PropertyRecord({name: prop, value: value}, prop);
72629             this.store.add(r);
72630
72631         }
72632     },
72633     
72634     // private
72635     remove : function(prop){
72636         var r = this.getRec(prop);
72637         if(r){
72638             this.store.remove(r);
72639             delete this.source[prop];
72640         }
72641     },
72642     
72643     // private
72644     getRec : function(prop){
72645         return this.store.getById(prop);
72646     },
72647
72648     // protected - should only be called by the grid.  Use grid.getSource instead.
72649     getSource : function(){
72650         return this.source;
72651     }
72652 });
72653
72654 /**
72655  * @class Ext.grid.PropertyColumnModel
72656  * @extends Ext.grid.ColumnModel
72657  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.
72658  * @constructor
72659  * @param {Ext.grid.Grid} grid The grid this store will be bound to
72660  * @param {Object} source The source data config object
72661  */
72662 Ext.grid.PropertyColumnModel = Ext.extend(Ext.grid.ColumnModel, {
72663     // private - strings used for locale support
72664     nameText : 'Name',
72665     valueText : 'Value',
72666     dateFormat : 'm/j/Y',
72667     trueText: 'true',
72668     falseText: 'false',
72669     
72670     constructor : function(grid, store){
72671         var g = Ext.grid,
72672                 f = Ext.form;
72673                 
72674             this.grid = grid;
72675             g.PropertyColumnModel.superclass.constructor.call(this, [
72676                 {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
72677                 {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
72678             ]);
72679             this.store = store;
72680         
72681             var bfield = new f.Field({
72682                 autoCreate: {tag: 'select', children: [
72683                     {tag: 'option', value: 'true', html: this.trueText},
72684                     {tag: 'option', value: 'false', html: this.falseText}
72685                 ]},
72686                 getValue : function(){
72687                     return this.el.dom.value == 'true';
72688                 }
72689             });
72690             this.editors = {
72691                 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
72692                 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
72693                 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
72694                 'boolean' : new g.GridEditor(bfield, {
72695                     autoSize: 'both'
72696                 })
72697             };
72698             this.renderCellDelegate = this.renderCell.createDelegate(this);
72699             this.renderPropDelegate = this.renderProp.createDelegate(this);
72700     },
72701
72702     // private
72703     renderDate : function(dateVal){
72704         return dateVal.dateFormat(this.dateFormat);
72705     },
72706
72707     // private
72708     renderBool : function(bVal){
72709         return this[bVal ? 'trueText' : 'falseText'];
72710     },
72711
72712     // private
72713     isCellEditable : function(colIndex, rowIndex){
72714         return colIndex == 1;
72715     },
72716
72717     // private
72718     getRenderer : function(col){
72719         return col == 1 ?
72720             this.renderCellDelegate : this.renderPropDelegate;
72721     },
72722
72723     // private
72724     renderProp : function(v){
72725         return this.getPropertyName(v);
72726     },
72727
72728     // private
72729     renderCell : function(val, meta, rec){
72730         var renderer = this.grid.customRenderers[rec.get('name')];
72731         if(renderer){
72732             return renderer.apply(this, arguments);
72733         }
72734         var rv = val;
72735         if(Ext.isDate(val)){
72736             rv = this.renderDate(val);
72737         }else if(typeof val == 'boolean'){
72738             rv = this.renderBool(val);
72739         }
72740         return Ext.util.Format.htmlEncode(rv);
72741     },
72742
72743     // private
72744     getPropertyName : function(name){
72745         var pn = this.grid.propertyNames;
72746         return pn && pn[name] ? pn[name] : name;
72747     },
72748
72749     // private
72750     getCellEditor : function(colIndex, rowIndex){
72751         var p = this.store.getProperty(rowIndex),
72752             n = p.data.name, 
72753             val = p.data.value;
72754         if(this.grid.customEditors[n]){
72755             return this.grid.customEditors[n];
72756         }
72757         if(Ext.isDate(val)){
72758             return this.editors.date;
72759         }else if(typeof val == 'number'){
72760             return this.editors.number;
72761         }else if(typeof val == 'boolean'){
72762             return this.editors['boolean'];
72763         }else{
72764             return this.editors.string;
72765         }
72766     },
72767
72768     // inherit docs
72769     destroy : function(){
72770         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);
72771         for(var ed in this.editors){
72772             Ext.destroy(this.editors[ed]);
72773         }
72774     }
72775 });
72776
72777 /**
72778  * @class Ext.grid.PropertyGrid
72779  * @extends Ext.grid.EditorGridPanel
72780  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
72781  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
72782  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:
72783  * <pre><code>
72784 var grid = new Ext.grid.PropertyGrid({
72785     title: 'Properties Grid',
72786     autoHeight: true,
72787     width: 300,
72788     renderTo: 'grid-ct',
72789     source: {
72790         "(name)": "My Object",
72791         "Created": new Date(Date.parse('10/15/2006')),
72792         "Available": false,
72793         "Version": .01,
72794         "Description": "A test object"
72795     }
72796 });
72797 </code></pre>
72798  * @constructor
72799  * @param {Object} config The grid config object
72800  */
72801 Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
72802     /**
72803     * @cfg {Object} propertyNames An object containing property name/display name pairs.
72804     * If specified, the display name will be shown in the name column instead of the property name.
72805     */
72806     /**
72807     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
72808     */
72809     /**
72810     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
72811     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
72812     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
72813     * associated with a custom input control by specifying a custom editor.  The name of the editor
72814     * type should correspond with the name of the property that will use the editor.  Example usage:
72815     * <pre><code>
72816 var grid = new Ext.grid.PropertyGrid({
72817     ...
72818     customEditors: {
72819         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
72820     },
72821     source: {
72822         'Start Time': '10:00 AM'
72823     }
72824 });
72825 </code></pre>
72826     */
72827     /**
72828     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
72829     */
72830     /**
72831     * @cfg {Object} customRenderers An object containing name/value pairs of custom renderer type definitions that allow
72832     * the grid to support custom rendering of fields.  By default, the grid supports strongly-typed rendering
72833     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
72834     * associated with the type of the value.  The name of the renderer type should correspond with the name of the property
72835     * that it will render.  Example usage:
72836     * <pre><code>
72837 var grid = new Ext.grid.PropertyGrid({
72838     ...
72839     customRenderers: {
72840         Available: function(v){
72841             if(v){
72842                 return '<span style="color: green;">Yes</span>';
72843             }else{
72844                 return '<span style="color: red;">No</span>';
72845             }
72846         }
72847     },
72848     source: {
72849         Available: true
72850     }
72851 });
72852 </code></pre>
72853     */
72854
72855     // private config overrides
72856     enableColumnMove:false,
72857     stripeRows:false,
72858     trackMouseOver: false,
72859     clicksToEdit:1,
72860     enableHdMenu : false,
72861     viewConfig : {
72862         forceFit:true
72863     },
72864
72865     // private
72866     initComponent : function(){
72867         this.customRenderers = this.customRenderers || {};
72868         this.customEditors = this.customEditors || {};
72869         this.lastEditRow = null;
72870         var store = new Ext.grid.PropertyStore(this);
72871         this.propStore = store;
72872         var cm = new Ext.grid.PropertyColumnModel(this, store);
72873         store.store.sort('name', 'ASC');
72874         this.addEvents(
72875             /**
72876              * @event beforepropertychange
72877              * Fires before a property value changes.  Handlers can return false to cancel the property change
72878              * (this will internally call {@link Ext.data.Record#reject} on the property's record).
72879              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
72880              * as the {@link #source} config property).
72881              * @param {String} recordId The record's id in the data store
72882              * @param {Mixed} value The current edited property value
72883              * @param {Mixed} oldValue The original property value prior to editing
72884              */
72885             'beforepropertychange',
72886             /**
72887              * @event propertychange
72888              * Fires after a property value has changed.
72889              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
72890              * as the {@link #source} config property).
72891              * @param {String} recordId The record's id in the data store
72892              * @param {Mixed} value The current edited property value
72893              * @param {Mixed} oldValue The original property value prior to editing
72894              */
72895             'propertychange'
72896         );
72897         this.cm = cm;
72898         this.ds = store.store;
72899         Ext.grid.PropertyGrid.superclass.initComponent.call(this);
72900
72901                 this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){
72902             if(colIndex === 0){
72903                 this.startEditing.defer(200, this, [rowIndex, 1]);
72904                 return false;
72905             }
72906         }, this);
72907     },
72908
72909     // private
72910     onRender : function(){
72911         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
72912
72913         this.getGridEl().addClass('x-props-grid');
72914     },
72915
72916     // private
72917     afterRender: function(){
72918         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
72919         if(this.source){
72920             this.setSource(this.source);
72921         }
72922     },
72923
72924     /**
72925      * Sets the source data object containing the property data.  The data object can contain one or more name/value
72926      * pairs representing all of the properties of an object to display in the grid, and this data will automatically
72927      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
72928      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
72929      * existing data.  See also the {@link #source} config value.  Example usage:
72930      * <pre><code>
72931 grid.setSource({
72932     "(name)": "My Object",
72933     "Created": new Date(Date.parse('10/15/2006')),  // date type
72934     "Available": false,  // boolean type
72935     "Version": .01,      // decimal type
72936     "Description": "A test object"
72937 });
72938 </code></pre>
72939      * @param {Object} source The data object
72940      */
72941     setSource : function(source){
72942         this.propStore.setSource(source);
72943     },
72944
72945     /**
72946      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
72947      * format of the data object.
72948      * @return {Object} The data object
72949      */
72950     getSource : function(){
72951         return this.propStore.getSource();
72952     },
72953     
72954     /**
72955      * Sets the value of a property.
72956      * @param {String} prop The name of the property to set
72957      * @param {Mixed} value The value to test
72958      * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to <tt>false</tt>.
72959      */
72960     setProperty : function(prop, value, create){
72961         this.propStore.setValue(prop, value, create);    
72962     },
72963     
72964     /**
72965      * Removes a property from the grid.
72966      * @param {String} prop The name of the property to remove
72967      */
72968     removeProperty : function(prop){
72969         this.propStore.remove(prop);
72970     }
72971
72972     /**
72973      * @cfg store
72974      * @hide
72975      */
72976     /**
72977      * @cfg colModel
72978      * @hide
72979      */
72980     /**
72981      * @cfg cm
72982      * @hide
72983      */
72984     /**
72985      * @cfg columns
72986      * @hide
72987      */
72988 });
72989 Ext.reg("propertygrid", Ext.grid.PropertyGrid);
72990 /**
72991  * @class Ext.grid.GroupingView
72992  * @extends Ext.grid.GridView
72993  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}
72994  * must be used to enable grouping.  Some grouping characteristics may also be configured at the
72995  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>
72996  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</code></li>
72997  * <li><code>{@link Ext.grid.Column#groupable groupable}</code></li>
72998  * <li><code>{@link Ext.grid.Column#groupName groupName}</code></li>
72999  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</code></li>
73000  * </ul></div>
73001  * <p>Sample usage:</p>
73002  * <pre><code>
73003 var grid = new Ext.grid.GridPanel({
73004     // A groupingStore is required for a GroupingView
73005     store: new {@link Ext.data.GroupingStore}({
73006         autoDestroy: true,
73007         reader: reader,
73008         data: xg.dummyData,
73009         sortInfo: {field: 'company', direction: 'ASC'},
73010         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,
73011         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,
73012         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'
73013     }),
73014     colModel: new {@link Ext.grid.ColumnModel}({
73015         columns:[
73016             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},
73017             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level
73018             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},
73019             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},
73020             {header: 'Industry', dataIndex: 'industry'},
73021             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
73022         ],
73023         defaults: {
73024             sortable: true,
73025             menuDisabled: false,
73026             width: 20
73027         }
73028     }),
73029
73030     view: new Ext.grid.GroupingView({
73031         {@link Ext.grid.GridView#forceFit forceFit}: true,
73032         // custom grouping text template to display the number of items per group
73033         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
73034     }),
73035
73036     frame:true,
73037     width: 700,
73038     height: 450,
73039     collapsible: true,
73040     animCollapse: false,
73041     title: 'Grouping Example',
73042     iconCls: 'icon-grid',
73043     renderTo: document.body
73044 });
73045  * </code></pre>
73046  * @constructor
73047  * @param {Object} config
73048  */
73049 Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {
73050
73051     /**
73052      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column
73053      * (defaults to 'Group By This Field').
73054      */
73055     groupByText : 'Group By This Field',
73056     /**
73057      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
73058      * (defaults to 'Show in Groups').
73059      */
73060     showGroupsText : 'Show in Groups',
73061     /**
73062      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)
73063      */
73064     hideGroupedColumn : false,
73065     /**
73066      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value
73067      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>
73068      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the
73069      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).
73070      */
73071     showGroupName : true,
73072     /**
73073      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
73074      */
73075     startCollapsed : false,
73076     /**
73077      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)
73078      */
73079     enableGrouping : true,
73080     /**
73081      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)
73082      */
73083     enableGroupingMenu : true,
73084     /**
73085      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
73086      */
73087     enableNoGroups : true,
73088     /**
73089      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).
73090      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.
73091      */
73092     emptyGroupText : '(None)',
73093     /**
73094      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)
73095      */
73096     ignoreAdd : false,
73097     /**
73098      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).
73099      * This is used to format an object which contains the following properties:
73100      * <div class="mdetail-params"><ul>
73101      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.
73102      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>
73103      * is specified, it is the result of a call to that function.</p></li>
73104      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>
73105      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>
73106      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>
73107      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the
73108      * View Element which contains the group.</p></li>
73109      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
73110      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data
73111      * for the row which caused group change.</p></li>
73112      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>
73113      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>
73114      * </ul></div></p>
73115      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>
73116 var grid = new Ext.grid.GridPanel({
73117     ...
73118     view: new Ext.grid.GroupingView({
73119         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
73120     }),
73121 });
73122      * </code></pre>
73123      */
73124     groupTextTpl : '{text}',
73125
73126     /**
73127      * @cfg {String} groupMode Indicates how to construct the group identifier. <tt>'value'</tt> constructs the id using
73128      * raw value, <tt>'display'</tt> constructs the id using the rendered value. Defaults to <tt>'value'</tt>.
73129      */
73130     groupMode: 'value',
73131
73132     /**
73133      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for
73134      * each column.
73135      */
73136
73137     // private
73138     initTemplates : function(){
73139         Ext.grid.GroupingView.superclass.initTemplates.call(this);
73140         this.state = {};
73141
73142         var sm = this.grid.getSelectionModel();
73143         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
73144                 this.onBeforeRowSelect, this);
73145
73146         if(!this.startGroup){
73147             this.startGroup = new Ext.XTemplate(
73148                 '<div id="{groupId}" class="x-grid-group {cls}">',
73149                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
73150                     '<div id="{groupId}-bd" class="x-grid-group-body">'
73151             );
73152         }
73153         this.startGroup.compile();
73154
73155         if (!this.endGroup) {
73156             this.endGroup = '</div></div>';
73157         }
73158     },
73159
73160     // private
73161     findGroup : function(el){
73162         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
73163     },
73164
73165     // private
73166     getGroups : function(){
73167         return this.hasRows() ? this.mainBody.dom.childNodes : [];
73168     },
73169
73170     // private
73171     onAdd : function(ds, records, index) {
73172         if (this.canGroup() && !this.ignoreAdd) {
73173             var ss = this.getScrollState();
73174             this.fireEvent('beforerowsinserted', ds, index, index + (records.length-1));
73175             this.refresh();
73176             this.restoreScroll(ss);
73177             this.fireEvent('rowsinserted', ds, index, index + (records.length-1));
73178         } else if (!this.canGroup()) {
73179             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);
73180         }
73181     },
73182
73183     // private
73184     onRemove : function(ds, record, index, isUpdate){
73185         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);
73186         var g = document.getElementById(record._groupId);
73187         if(g && g.childNodes[1].childNodes.length < 1){
73188             Ext.removeNode(g);
73189         }
73190         this.applyEmptyText();
73191     },
73192
73193     // private
73194     refreshRow : function(record){
73195         if(this.ds.getCount()==1){
73196             this.refresh();
73197         }else{
73198             this.isUpdating = true;
73199             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);
73200             this.isUpdating = false;
73201         }
73202     },
73203
73204     // private
73205     beforeMenuShow : function(){
73206         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;
73207         if((item = items.get('groupBy'))){
73208             item.setDisabled(disabled);
73209         }
73210         if((item = items.get('showGroups'))){
73211             item.setDisabled(disabled);
73212             item.setChecked(this.enableGrouping, true);
73213         }
73214     },
73215
73216     // private
73217     renderUI : function(){
73218         Ext.grid.GroupingView.superclass.renderUI.call(this);
73219         this.mainBody.on('mousedown', this.interceptMouse, this);
73220
73221         if(this.enableGroupingMenu && this.hmenu){
73222             this.hmenu.add('-',{
73223                 itemId:'groupBy',
73224                 text: this.groupByText,
73225                 handler: this.onGroupByClick,
73226                 scope: this,
73227                 iconCls:'x-group-by-icon'
73228             });
73229             if(this.enableNoGroups){
73230                 this.hmenu.add({
73231                     itemId:'showGroups',
73232                     text: this.showGroupsText,
73233                     checked: true,
73234                     checkHandler: this.onShowGroupsClick,
73235                     scope: this
73236                 });
73237             }
73238             this.hmenu.on('beforeshow', this.beforeMenuShow, this);
73239         }
73240     },
73241
73242     processEvent: function(name, e){
73243         Ext.grid.GroupingView.superclass.processEvent.call(this, name, e);
73244         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
73245         if(hd){
73246             // group value is at the end of the string
73247             var field = this.getGroupField(),
73248                 prefix = this.getPrefix(field),
73249                 groupValue = hd.id.substring(prefix.length);
73250
73251             // remove trailing '-hd'
73252             groupValue = groupValue.substr(0, groupValue.length - 3);
73253             if(groupValue){
73254                 this.grid.fireEvent('group' + name, this.grid, field, groupValue, e);
73255             }
73256         }
73257
73258     },
73259
73260     // private
73261     onGroupByClick : function(){
73262         this.enableGrouping = true;
73263         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
73264         this.grid.fireEvent('groupchange', this, this.grid.store.getGroupState());
73265         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
73266         this.refresh();
73267     },
73268
73269     // private
73270     onShowGroupsClick : function(mi, checked){
73271         this.enableGrouping = checked;
73272         if(checked){
73273             this.onGroupByClick();
73274         }else{
73275             this.grid.store.clearGrouping();
73276             this.grid.fireEvent('groupchange', this, null);
73277         }
73278     },
73279
73280     /**
73281      * Toggle the group that contains the specific row.
73282      * @param {Number} rowIndex The row inside the group
73283      * @param {Boolean} expanded (optional)
73284      */
73285     toggleRowIndex : function(rowIndex, expanded){
73286         if(!this.canGroup()){
73287             return;
73288         }
73289         var row = this.getRow(rowIndex);
73290         if(row){
73291             this.toggleGroup(this.findGroup(row), expanded);
73292         }
73293     },
73294
73295     /**
73296      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.
73297      * @param {String} groupId The groupId assigned to the group (see getGroupId)
73298      * @param {Boolean} expanded (optional)
73299      */
73300     toggleGroup : function(group, expanded){
73301         var gel = Ext.get(group);
73302         expanded = Ext.isDefined(expanded) ? expanded : gel.hasClass('x-grid-group-collapsed');
73303         if(this.state[gel.id] !== expanded){
73304             this.grid.stopEditing(true);
73305             this.state[gel.id] = expanded;
73306             gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
73307         }
73308     },
73309
73310     /**
73311      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.
73312      * @param {Boolean} expanded (optional)
73313      */
73314     toggleAllGroups : function(expanded){
73315         var groups = this.getGroups();
73316         for(var i = 0, len = groups.length; i < len; i++){
73317             this.toggleGroup(groups[i], expanded);
73318         }
73319     },
73320
73321     /**
73322      * Expands all grouped rows.
73323      */
73324     expandAllGroups : function(){
73325         this.toggleAllGroups(true);
73326     },
73327
73328     /**
73329      * Collapses all grouped rows.
73330      */
73331     collapseAllGroups : function(){
73332         this.toggleAllGroups(false);
73333     },
73334
73335     // private
73336     interceptMouse : function(e){
73337         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
73338         if(hd){
73339             e.stopEvent();
73340             this.toggleGroup(hd.parentNode);
73341         }
73342     },
73343
73344     // private
73345     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){
73346         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);
73347         if(g === '' || g === '&#160;'){
73348             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
73349         }
73350         return g;
73351     },
73352
73353     // private
73354     getGroupField : function(){
73355         return this.grid.store.getGroupState();
73356     },
73357
73358     // private
73359     afterRender : function(){
73360         if(!this.ds || !this.cm){
73361             return;
73362         }
73363         Ext.grid.GroupingView.superclass.afterRender.call(this);
73364         if(this.grid.deferRowRender){
73365             this.updateGroupWidths();
73366         }
73367     },
73368
73369     // private
73370     renderRows : function(){
73371         var groupField = this.getGroupField();
73372         var eg = !!groupField;
73373         // if they turned off grouping and the last grouped field is hidden
73374         if(this.hideGroupedColumn) {
73375             var colIndex = this.cm.findColumnIndex(groupField),
73376                 hasLastGroupField = Ext.isDefined(this.lastGroupField);
73377             if(!eg && hasLastGroupField){
73378                 this.mainBody.update('');
73379                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);
73380                 delete this.lastGroupField;
73381             }else if (eg && !hasLastGroupField){
73382                 this.lastGroupField = groupField;
73383                 this.cm.setHidden(colIndex, true);
73384             }else if (eg && hasLastGroupField && groupField !== this.lastGroupField) {
73385                 this.mainBody.update('');
73386                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
73387                 this.cm.setHidden(oldIndex, false);
73388                 this.lastGroupField = groupField;
73389                 this.cm.setHidden(colIndex, true);
73390             }
73391         }
73392         return Ext.grid.GroupingView.superclass.renderRows.apply(
73393                     this, arguments);
73394     },
73395
73396     // private
73397     doRender : function(cs, rs, ds, startRow, colCount, stripe){
73398         if(rs.length < 1){
73399             return '';
73400         }
73401
73402         if(!this.canGroup() || this.isUpdating){
73403             return Ext.grid.GroupingView.superclass.doRender.apply(this, arguments);
73404         }
73405
73406         var groupField = this.getGroupField(),
73407             colIndex = this.cm.findColumnIndex(groupField),
73408             g,
73409             gstyle = 'width:' + this.getTotalWidth() + ';',
73410             cfg = this.cm.config[colIndex],
73411             groupRenderer = cfg.groupRenderer || cfg.renderer,
73412             prefix = this.showGroupName ? (cfg.groupName || cfg.header)+': ' : '',
73413             groups = [],
73414             curGroup, i, len, gid;
73415
73416         for(i = 0, len = rs.length; i < len; i++){
73417             var rowIndex = startRow + i,
73418                 r = rs[i],
73419                 gvalue = r.data[groupField];
73420
73421                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);
73422             if(!curGroup || curGroup.group != g){
73423                 gid = this.constructId(gvalue, groupField, colIndex);
73424                 // if state is defined use it, however state is in terms of expanded
73425                 // so negate it, otherwise use the default.
73426                 this.state[gid] = !(Ext.isDefined(this.state[gid]) ? !this.state[gid] : this.startCollapsed);
73427                 curGroup = {
73428                     group: g,
73429                     gvalue: gvalue,
73430                     text: prefix + g,
73431                     groupId: gid,
73432                     startRow: rowIndex,
73433                     rs: [r],
73434                     cls: this.state[gid] ? '' : 'x-grid-group-collapsed',
73435                     style: gstyle
73436                 };
73437                 groups.push(curGroup);
73438             }else{
73439                 curGroup.rs.push(r);
73440             }
73441             r._groupId = gid;
73442         }
73443
73444         var buf = [];
73445         for(i = 0, len = groups.length; i < len; i++){
73446             g = groups[i];
73447             this.doGroupStart(buf, g, cs, ds, colCount);
73448             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(
73449                     this, cs, g.rs, ds, g.startRow, colCount, stripe);
73450
73451             this.doGroupEnd(buf, g, cs, ds, colCount);
73452         }
73453         return buf.join('');
73454     },
73455
73456     /**
73457      * Dynamically tries to determine the groupId of a specific value
73458      * @param {String} value
73459      * @return {String} The group id
73460      */
73461     getGroupId : function(value){
73462         var field = this.getGroupField();
73463         return this.constructId(value, field, this.cm.findColumnIndex(field));
73464     },
73465
73466     // private
73467     constructId : function(value, field, idx){
73468         var cfg = this.cm.config[idx],
73469             groupRenderer = cfg.groupRenderer || cfg.renderer,
73470             val = (this.groupMode == 'value') ? value : this.getGroup(value, {data:{}}, groupRenderer, 0, idx, this.ds);
73471
73472         return this.getPrefix(field) + Ext.util.Format.htmlEncode(val);
73473     },
73474
73475     // private
73476     canGroup  : function(){
73477         return this.enableGrouping && !!this.getGroupField();
73478     },
73479
73480     // private
73481     getPrefix: function(field){
73482         return this.grid.getGridEl().id + '-gp-' + field + '-';
73483     },
73484
73485     // private
73486     doGroupStart : function(buf, g, cs, ds, colCount){
73487         buf[buf.length] = this.startGroup.apply(g);
73488     },
73489
73490     // private
73491     doGroupEnd : function(buf, g, cs, ds, colCount){
73492         buf[buf.length] = this.endGroup;
73493     },
73494
73495     // private
73496     getRows : function(){
73497         if(!this.canGroup()){
73498             return Ext.grid.GroupingView.superclass.getRows.call(this);
73499         }
73500         var r = [],
73501             gs = this.getGroups(),
73502             g,
73503             i = 0,
73504             len = gs.length,
73505             j,
73506             jlen;
73507         for(; i < len; ++i){
73508             g = gs[i].childNodes[1];
73509             if(g){
73510                 g = g.childNodes;
73511                 for(j = 0, jlen = g.length; j < jlen; ++j){
73512                     r[r.length] = g[j];
73513                 }
73514             }
73515         }
73516         return r;
73517     },
73518
73519     // private
73520     updateGroupWidths : function(){
73521         if(!this.canGroup() || !this.hasRows()){
73522             return;
73523         }
73524         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.getScrollOffset()) +'px';
73525         var gs = this.getGroups();
73526         for(var i = 0, len = gs.length; i < len; i++){
73527             gs[i].firstChild.style.width = tw;
73528         }
73529     },
73530
73531     // private
73532     onColumnWidthUpdated : function(col, w, tw){
73533         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);
73534         this.updateGroupWidths();
73535     },
73536
73537     // private
73538     onAllColumnWidthsUpdated : function(ws, tw){
73539         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);
73540         this.updateGroupWidths();
73541     },
73542
73543     // private
73544     onColumnHiddenUpdated : function(col, hidden, tw){
73545         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);
73546         this.updateGroupWidths();
73547     },
73548
73549     // private
73550     onLayout : function(){
73551         this.updateGroupWidths();
73552     },
73553
73554     // private
73555     onBeforeRowSelect : function(sm, rowIndex){
73556         this.toggleRowIndex(rowIndex, true);
73557     }
73558 });
73559 // private
73560 Ext.grid.GroupingView.GROUP_ID = 1000;