6f89c4e146c97b641e0389a80a197d63214c2ca8
[extjs.git] / ext-all-debug-w-comments.js
1 /*!
2  * Ext JS Library 3.2.1
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.DomHelper
9  * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10  * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11  * from your DOM building code.</p>
12  *
13  * <p><b><u>DomHelper element specification object</u></b></p>
14  * <p>A specification object is used when creating elements. Attributes of this object
15  * are assumed to be element attributes, except for 4 special attributes:
16  * <div class="mdetail-params"><ul>
17  * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18  * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19  * same kind of element definition objects to be created and appended. These can be nested
20  * as deep as you want.</div></li>
21  * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22  * This will end up being either the "class" attribute on a HTML fragment or className
23  * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24  * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
25  * </ul></div></p>
26  *
27  * <p><b><u>Insertion methods</u></b></p>
28  * <p>Commonly used insertion methods:
29  * <div class="mdetail-params"><ul>
30  * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31  * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32  * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33  * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34  * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35  * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
36  * </ul></div></p>
37  *
38  * <p><b><u>Example</u></b></p>
39  * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40  * element with id <tt>'my-div'</tt>:<br>
41  <pre><code>
42 var dh = Ext.DomHelper; // create shorthand alias
43 // specification object
44 var spec = {
45     id: 'my-ul',
46     tag: 'ul',
47     cls: 'my-list',
48     // append children after creating
49     children: [     // may also specify 'cn' instead of 'children'
50         {tag: 'li', id: 'item0', html: 'List Item 0'},
51         {tag: 'li', id: 'item1', html: 'List Item 1'},
52         {tag: 'li', id: 'item2', html: 'List Item 2'}
53     ]
54 };
55 var list = dh.append(
56     'my-div', // the context element 'my-div' can either be the id or the actual node
57     spec      // the specification object
58 );
59  </code></pre></p>
60  * <p>Element creation specification parameters in this class may also be passed as an Array of
61  * specification objects. This can be used to insert multiple sibling nodes into an existing
62  * container very efficiently. For example, to add more list items to the example above:<pre><code>
63 dh.append('my-ul', [
64     {tag: 'li', id: 'item3', html: 'List Item 3'},
65     {tag: 'li', id: 'item4', html: 'List Item 4'}
66 ]);
67  * </code></pre></p>
68  *
69  * <p><b><u>Templating</u></b></p>
70  * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71  * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72  * insert new elements. Revisiting the example above, we could utilize templating this time:
73  * <pre><code>
74 // create the node
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
76 // get template
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
78
79 for(var i = 0; i < 5, i++){
80     tpl.append(list, [i]); // use template to append to the actual node
81 }
82  * </code></pre></p>
83  * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
85
86 var tpl = new Ext.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack&#39;s Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin&#39;s Site"]);
89  * </code></pre></p>
90  *
91  * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
93
94 var tpl = new Ext.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
96     id: 'link1',
97     url: 'http://www.jackslocum.com/',
98     text: "Jack&#39;s Site"
99 });
100 tpl.append('blog-roll', {
101     id: 'link2',
102     url: 'http://www.dustindiaz.com/',
103     text: "Dustin&#39;s Site"
104 });
105  * </code></pre></p>
106  *
107  * <p><b><u>Compiling Templates</u></b></p>
108  * <p>Templates are applied using regular expressions. The performance is great, but if
109  * you are adding a bunch of DOM elements using the same template, you can increase
110  * performance even further by {@link Ext.Template#compile "compiling"} the template.
111  * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112  * broken up at the different variable points and a dynamic function is created and eval'ed.
113  * The generated function performs string concatenation of these parts and the passed
114  * variables instead of using regular expressions.
115  * <pre><code>
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
117
118 var tpl = new Ext.DomHelper.createTemplate(html);
119 tpl.compile();
120
121 //... use template like normal
122  * </code></pre></p>
123  *
124  * <p><b><u>Performance Boost</u></b></p>
125  * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126  * of DOM can significantly boost performance.</p>
127  * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128  * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129  * results in the creation of a text node. Usage:</p>
130  * <pre><code>
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
132  * </code></pre>
133  * @singleton
134  */
135 Ext.DomHelper = function(){
136     var tempTableEl = null,
137         emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
138         tableRe = /^table|tbody|tr|td$/i,
139         confRe = /tag|children|cn|html$/i,
140         tableElRe = /td|tr|tbody/i,
141         cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
142         endRe = /end/i,
143         pub,
144         // kill repeat to save bytes
145         afterbegin = 'afterbegin',
146         afterend = 'afterend',
147         beforebegin = 'beforebegin',
148         beforeend = 'beforeend',
149         ts = '<table>',
150         te = '</table>',
151         tbs = ts+'<tbody>',
152         tbe = '</tbody>'+te,
153         trs = tbs + '<tr>',
154         tre = '</tr>'+tbe;
155
156     // private
157     function doInsert(el, o, returnElement, pos, sibling, append){
158         var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
159         return returnElement ? Ext.get(newNode, true) : newNode;
160     }
161
162     // build as innerHTML where available
163     function createHtml(o){
164         var b = '',
165             attr,
166             val,
167             key,
168             keyVal,
169             cn;
170
171         if(typeof o == "string"){
172             b = o;
173         } else if (Ext.isArray(o)) {
174             for (var i=0; i < o.length; i++) {
175                 if(o[i]) {
176                     b += createHtml(o[i]);
177                 }
178             };
179         } else {
180             b += '<' + (o.tag = o.tag || 'div');
181             for (attr in o) {
182                 val = o[attr];
183                 if(!confRe.test(attr)){
184                     if (typeof val == "object") {
185                         b += ' ' + attr + '="';
186                         for (key in val) {
187                             b += key + ':' + val[key] + ';';
188                         };
189                         b += '"';
190                     }else{
191                         b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
192                     }
193                 }
194             };
195             // Now either just close the tag or try to add children and close the tag.
196             if (emptyTags.test(o.tag)) {
197                 b += '/>';
198             } else {
199                 b += '>';
200                 if ((cn = o.children || o.cn)) {
201                     b += createHtml(cn);
202                 } else if(o.html){
203                     b += o.html;
204                 }
205                 b += '</' + o.tag + '>';
206             }
207         }
208         return b;
209     }
210
211     function ieTable(depth, s, h, e){
212         tempTableEl.innerHTML = [s, h, e].join('');
213         var i = -1,
214             el = tempTableEl,
215             ns;
216         while(++i < depth){
217             el = el.firstChild;
218         }
219 //      If the result is multiple siblings, then encapsulate them into one fragment.
220         if(ns = el.nextSibling){
221             var df = document.createDocumentFragment();
222             while(el){
223                 ns = el.nextSibling;
224                 df.appendChild(el);
225                 el = ns;
226             }
227             el = df;
228         }
229         return el;
230     }
231
232     /**
233      * @ignore
234      * Nasty code for IE's broken table implementation
235      */
236     function insertIntoTable(tag, where, el, html) {
237         var node,
238             before;
239
240         tempTableEl = tempTableEl || document.createElement('div');
241
242         if(tag == 'td' && (where == afterbegin || where == beforeend) ||
243            !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
244             return;
245         }
246         before = where == beforebegin ? el :
247                  where == afterend ? el.nextSibling :
248                  where == afterbegin ? el.firstChild : null;
249
250         if (where == beforebegin || where == afterend) {
251             el = el.parentNode;
252         }
253
254         if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
255             node = ieTable(4, trs, html, tre);
256         } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
257                    (tag == 'tr' && (where == beforebegin || where == afterend))) {
258             node = ieTable(3, tbs, html, tbe);
259         } else {
260             node = ieTable(2, ts, html, te);
261         }
262         el.insertBefore(node, before);
263         return node;
264     }
265
266
267     pub = {
268         /**
269          * Returns the markup for the passed Element(s) config.
270          * @param {Object} o The DOM object spec (and children)
271          * @return {String}
272          */
273         markup : function(o){
274             return createHtml(o);
275         },
276
277         /**
278          * Applies a style specification to an element.
279          * @param {String/HTMLElement} el The element to apply styles to
280          * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
281          * a function which returns such a specification.
282          */
283         applyStyles : function(el, styles){
284             if(styles){
285                 var i = 0,
286                     len,
287                     style,
288                     matches;
289
290                 el = Ext.fly(el);
291                 if(typeof styles == "function"){
292                     styles = styles.call();
293                 }
294                 if(typeof styles == "string"){
295                     while((matches = cssRe.exec(styles))){
296                         el.setStyle(matches[1], matches[2]);
297                     }
298                 }else if (typeof styles == "object"){
299                     el.setStyle(styles);
300                 }
301             }
302         },
303
304         /**
305          * Inserts an HTML fragment into the DOM.
306          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
307          * @param {HTMLElement} el The context element
308          * @param {String} html The HTML fragment
309          * @return {HTMLElement} The new node
310          */
311         insertHtml : function(where, el, html){
312             var hash = {},
313                 hashVal,
314                 setStart,
315                 range,
316                 frag,
317                 rangeEl,
318                 rs;
319
320             where = where.toLowerCase();
321             // add these here because they are used in both branches of the condition.
322             hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
323             hash[afterend] = ['AfterEnd', 'nextSibling'];
324
325             if (el.insertAdjacentHTML) {
326                 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
327                     return rs;
328                 }
329                 // add these two to the hash.
330                 hash[afterbegin] = ['AfterBegin', 'firstChild'];
331                 hash[beforeend] = ['BeforeEnd', 'lastChild'];
332                 if ((hashVal = hash[where])) {
333                     el.insertAdjacentHTML(hashVal[0], html);
334                     return el[hashVal[1]];
335                 }
336             } else {
337                 range = el.ownerDocument.createRange();
338                 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
339                 if (hash[where]) {
340                     range[setStart](el);
341                     frag = range.createContextualFragment(html);
342                     el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
343                     return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
344                 } else {
345                     rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
346                     if (el.firstChild) {
347                         range[setStart](el[rangeEl]);
348                         frag = range.createContextualFragment(html);
349                         if(where == afterbegin){
350                             el.insertBefore(frag, el.firstChild);
351                         }else{
352                             el.appendChild(frag);
353                         }
354                     } else {
355                         el.innerHTML = html;
356                     }
357                     return el[rangeEl];
358                 }
359             }
360             throw 'Illegal insertion point -> "' + where + '"';
361         },
362
363         /**
364          * Creates new DOM element(s) and inserts them before el.
365          * @param {Mixed} el The context element
366          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
367          * @param {Boolean} returnElement (optional) true to return a Ext.Element
368          * @return {HTMLElement/Ext.Element} The new node
369          */
370         insertBefore : function(el, o, returnElement){
371             return doInsert(el, o, returnElement, beforebegin);
372         },
373
374         /**
375          * Creates new DOM element(s) and inserts them after el.
376          * @param {Mixed} el The context element
377          * @param {Object} o The DOM object spec (and children)
378          * @param {Boolean} returnElement (optional) true to return a Ext.Element
379          * @return {HTMLElement/Ext.Element} The new node
380          */
381         insertAfter : function(el, o, returnElement){
382             return doInsert(el, o, returnElement, afterend, 'nextSibling');
383         },
384
385         /**
386          * Creates new DOM element(s) and inserts them as the first child of el.
387          * @param {Mixed} el The context element
388          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
389          * @param {Boolean} returnElement (optional) true to return a Ext.Element
390          * @return {HTMLElement/Ext.Element} The new node
391          */
392         insertFirst : function(el, o, returnElement){
393             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
394         },
395
396         /**
397          * Creates new DOM element(s) and appends them to el.
398          * @param {Mixed} el The context element
399          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
400          * @param {Boolean} returnElement (optional) true to return a Ext.Element
401          * @return {HTMLElement/Ext.Element} The new node
402          */
403         append : function(el, o, returnElement){
404             return doInsert(el, o, returnElement, beforeend, '', true);
405         },
406
407         /**
408          * Creates new DOM element(s) and overwrites the contents of el with them.
409          * @param {Mixed} el The context element
410          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
411          * @param {Boolean} returnElement (optional) true to return a Ext.Element
412          * @return {HTMLElement/Ext.Element} The new node
413          */
414         overwrite : function(el, o, returnElement){
415             el = Ext.getDom(el);
416             el.innerHTML = createHtml(o);
417             return returnElement ? Ext.get(el.firstChild) : el.firstChild;
418         },
419
420         createHtml : createHtml
421     };
422     return pub;
423 }();
424 /**
425  * @class Ext.DomHelper
426  */
427 Ext.apply(Ext.DomHelper,
428 function(){
429     var pub,
430         afterbegin = 'afterbegin',
431         afterend = 'afterend',
432         beforebegin = 'beforebegin',
433         beforeend = 'beforeend',
434         confRe = /tag|children|cn|html$/i;
435
436     // private
437     function doInsert(el, o, returnElement, pos, sibling, append){
438         el = Ext.getDom(el);
439         var newNode;
440         if (pub.useDom) {
441             newNode = createDom(o, null);
442             if (append) {
443                 el.appendChild(newNode);
444             } else {
445                 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
446             }
447         } else {
448             newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
449         }
450         return returnElement ? Ext.get(newNode, true) : newNode;
451     }
452
453     // build as dom
454     /** @ignore */
455     function createDom(o, parentNode){
456         var el,
457             doc = document,
458             useSet,
459             attr,
460             val,
461             cn;
462
463         if (Ext.isArray(o)) {                       // Allow Arrays of siblings to be inserted
464             el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
465             for (var i = 0, l = o.length; i < l; i++) {
466                 createDom(o[i], el);
467             }
468         } else if (typeof o == 'string') {         // Allow a string as a child spec.
469             el = doc.createTextNode(o);
470         } else {
471             el = doc.createElement( o.tag || 'div' );
472             useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
473             for (var attr in o) {
474                 if(!confRe.test(attr)){
475                     val = o[attr];
476                     if(attr == 'cls'){
477                         el.className = val;
478                     }else{
479                         if(useSet){
480                             el.setAttribute(attr, val);
481                         }else{
482                             el[attr] = val;
483                         }
484                     }
485                 }
486             }
487             Ext.DomHelper.applyStyles(el, o.style);
488
489             if ((cn = o.children || o.cn)) {
490                 createDom(cn, el);
491             } else if (o.html) {
492                 el.innerHTML = o.html;
493             }
494         }
495         if(parentNode){
496            parentNode.appendChild(el);
497         }
498         return el;
499     }
500
501     pub = {
502         /**
503          * Creates a new Ext.Template from the DOM object spec.
504          * @param {Object} o The DOM object spec (and children)
505          * @return {Ext.Template} The new template
506          */
507         createTemplate : function(o){
508             var html = Ext.DomHelper.createHtml(o);
509             return new Ext.Template(html);
510         },
511
512         /** True to force the use of DOM instead of html fragments @type Boolean */
513         useDom : false,
514
515         /**
516          * Creates new DOM element(s) and inserts them before el.
517          * @param {Mixed} el The context element
518          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
519          * @param {Boolean} returnElement (optional) true to return a Ext.Element
520          * @return {HTMLElement/Ext.Element} The new node
521          * @hide (repeat)
522          */
523         insertBefore : function(el, o, returnElement){
524             return doInsert(el, o, returnElement, beforebegin);
525         },
526
527         /**
528          * Creates new DOM element(s) and inserts them after el.
529          * @param {Mixed} el The context element
530          * @param {Object} o The DOM object spec (and children)
531          * @param {Boolean} returnElement (optional) true to return a Ext.Element
532          * @return {HTMLElement/Ext.Element} The new node
533          * @hide (repeat)
534          */
535         insertAfter : function(el, o, returnElement){
536             return doInsert(el, o, returnElement, afterend, 'nextSibling');
537         },
538
539         /**
540          * Creates new DOM element(s) and inserts them as the first child of el.
541          * @param {Mixed} el The context element
542          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
543          * @param {Boolean} returnElement (optional) true to return a Ext.Element
544          * @return {HTMLElement/Ext.Element} The new node
545          * @hide (repeat)
546          */
547         insertFirst : function(el, o, returnElement){
548             return doInsert(el, o, returnElement, afterbegin, 'firstChild');
549         },
550
551         /**
552          * Creates new DOM element(s) and appends them to el.
553          * @param {Mixed} el The context element
554          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
555          * @param {Boolean} returnElement (optional) true to return a Ext.Element
556          * @return {HTMLElement/Ext.Element} The new node
557          * @hide (repeat)
558          */
559         append: function(el, o, returnElement){
560             return doInsert(el, o, returnElement, beforeend, '', true);
561         },
562
563         /**
564          * Creates new DOM element(s) without inserting them to the document.
565          * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
566          * @return {HTMLElement} The new uninserted node
567          */
568         createDom: createDom
569     };
570     return pub;
571 }());
572 /**
573  * @class Ext.Template
574  * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
575  * for greater performance.</p>
576  * <p>For example usage {@link #Template see the constructor}.</p>
577  *
578  * @constructor
579  * An instance of this class may be created by passing to the constructor either
580  * a single argument, or multiple arguments:
581  * <div class="mdetail-params"><ul>
582  * <li><b>single argument</b> : String/Array
583  * <div class="sub-desc">
584  * The single argument may be either a String or an Array:<ul>
585  * <li><tt>String</tt> : </li><pre><code>
586 var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
587 t.{@link #append}('some-element', ['foo']);
588  * </code></pre>
589  * <li><tt>Array</tt> : </li>
590  * An Array will be combined with <code>join('')</code>.
591 <pre><code>
592 var t = new Ext.Template([
593     '&lt;div name="{id}"&gt;',
594         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
595     '&lt;/div&gt;',
596 ]);
597 t.{@link #compile}();
598 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
599 </code></pre>
600  * </ul></div></li>
601  * <li><b>multiple arguments</b> : String, Object, Array, ...
602  * <div class="sub-desc">
603  * Multiple arguments will be combined with <code>join('')</code>.
604  * <pre><code>
605 var t = new Ext.Template(
606     '&lt;div name="{id}"&gt;',
607         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
608     '&lt;/div&gt;',
609     // a configuration object:
610     {
611         compiled: true,      // {@link #compile} immediately
612         disableFormats: true // See Notes below.
613     }
614 );
615  * </code></pre>
616  * <p><b>Notes</b>:</p>
617  * <div class="mdetail-params"><ul>
618  * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
619  * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
620  * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
621  * when no formatting is required.</li>
622  * </ul></div>
623  * </div></li>
624  * </ul></div>
625  * @param {Mixed} config
626  */
627 Ext.Template = function(html){
628     var me = this,
629         a = arguments,
630         buf = [],
631         v;
632
633     if (Ext.isArray(html)) {
634         html = html.join("");
635     } else if (a.length > 1) {
636         for(var i = 0, len = a.length; i < len; i++){
637             v = a[i];
638             if(typeof v == 'object'){
639                 Ext.apply(me, v);
640             } else {
641                 buf.push(v);
642             }
643         };
644         html = buf.join('');
645     }
646
647     /**@private*/
648     me.html = html;
649     /**
650      * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
651      * immediately (see <code>{@link #compile}</code>).
652      * Defaults to <tt>false</tt>.
653      */
654     if (me.compiled) {
655         me.compile();
656     }
657 };
658 Ext.Template.prototype = {
659     /**
660      * @cfg {RegExp} re The regular expression used to match template variables.
661      * Defaults to:<pre><code>
662      * re : /\{([\w-]+)\}/g                                     // for Ext Core
663      * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g      // for Ext JS
664      * </code></pre>
665      */
666     re : /\{([\w-]+)\}/g,
667     /**
668      * See <code>{@link #re}</code>.
669      * @type RegExp
670      * @property re
671      */
672
673     /**
674      * Returns an HTML fragment of this template with the specified <code>values</code> applied.
675      * @param {Object/Array} values
676      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
677      * or an object (i.e. <code>{foo: 'bar'}</code>).
678      * @return {String} The HTML fragment
679      */
680     applyTemplate : function(values){
681         var me = this;
682
683         return me.compiled ?
684                 me.compiled(values) :
685                 me.html.replace(me.re, function(m, name){
686                     return values[name] !== undefined ? values[name] : "";
687                 });
688     },
689
690     /**
691      * Sets the HTML used as the template and optionally compiles it.
692      * @param {String} html
693      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
694      * @return {Ext.Template} this
695      */
696     set : function(html, compile){
697         var me = this;
698         me.html = html;
699         me.compiled = null;
700         return compile ? me.compile() : me;
701     },
702
703     /**
704      * Compiles the template into an internal function, eliminating the RegEx overhead.
705      * @return {Ext.Template} this
706      */
707     compile : function(){
708         var me = this,
709             sep = Ext.isGecko ? "+" : ",";
710
711         function fn(m, name){
712             name = "values['" + name + "']";
713             return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
714         }
715
716         eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
717              me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
718              (Ext.isGecko ?  "';};" : "'].join('');};"));
719         return me;
720     },
721
722     /**
723      * Applies the supplied values to the template and inserts the new node(s) as the first child of 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     insertFirst: function(el, values, returnElement){
730         return this.doInsert('afterBegin', el, values, returnElement);
731     },
732
733     /**
734      * Applies the supplied values to the template and inserts the new node(s) before 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     insertBefore: function(el, values, returnElement){
741         return this.doInsert('beforeBegin', el, values, returnElement);
742     },
743
744     /**
745      * Applies the supplied values to the template and inserts the new node(s) after el.
746      * @param {Mixed} el The context element
747      * @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'})
748      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
749      * @return {HTMLElement/Ext.Element} The new node or Element
750      */
751     insertAfter : function(el, values, returnElement){
752         return this.doInsert('afterEnd', el, values, returnElement);
753     },
754
755     /**
756      * Applies the supplied <code>values</code> to the template and appends
757      * the new node(s) to the specified <code>el</code>.
758      * <p>For example usage {@link #Template see the constructor}.</p>
759      * @param {Mixed} el The context element
760      * @param {Object/Array} values
761      * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
762      * or an object (i.e. <code>{foo: 'bar'}</code>).
763      * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
764      * @return {HTMLElement/Ext.Element} The new node or Element
765      */
766     append : function(el, values, returnElement){
767         return this.doInsert('beforeEnd', el, values, returnElement);
768     },
769
770     doInsert : function(where, el, values, returnEl){
771         el = Ext.getDom(el);
772         var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
773         return returnEl ? Ext.get(newNode, true) : newNode;
774     },
775
776     /**
777      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
778      * @param {Mixed} el The context element
779      * @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'})
780      * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
781      * @return {HTMLElement/Ext.Element} The new node or Element
782      */
783     overwrite : function(el, values, returnElement){
784         el = Ext.getDom(el);
785         el.innerHTML = this.applyTemplate(values);
786         return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
787     }
788 };
789 /**
790  * Alias for {@link #applyTemplate}
791  * Returns an HTML fragment of this template with the specified <code>values</code> applied.
792  * @param {Object/Array} values
793  * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
794  * or an object (i.e. <code>{foo: 'bar'}</code>).
795  * @return {String} The HTML fragment
796  * @member Ext.Template
797  * @method apply
798  */
799 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
800
801 /**
802  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
803  * @param {String/HTMLElement} el A DOM element or its id
804  * @param {Object} config A configuration object
805  * @return {Ext.Template} The created template
806  * @static
807  */
808 Ext.Template.from = function(el, config){
809     el = Ext.getDom(el);
810     return new Ext.Template(el.value || el.innerHTML, config || '');
811 };
812 /**
813  * @class Ext.Template
814  */
815 Ext.apply(Ext.Template.prototype, {
816     /**
817      * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
818      * functions in the template. If the template does not contain
819      * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
820      * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
821      * <pre><code>
822 var t = new Ext.Template(
823     '&lt;div name="{id}"&gt;',
824         '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
825     '&lt;/div&gt;',
826     {
827         compiled: true,      // {@link #compile} immediately
828         disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
829     }
830 );
831      * </code></pre>
832      * For a list of available format functions, see {@link Ext.util.Format}.
833      */
834     disableFormats : false,
835     /**
836      * See <code>{@link #disableFormats}</code>.
837      * @type Boolean
838      * @property disableFormats
839      */
840
841     /**
842      * The regular expression used to match template variables
843      * @type RegExp
844      * @property
845      * @hide repeat doc
846      */
847     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
848     argsRe : /^\s*['"](.*)["']\s*$/,
849     compileARe : /\\/g,
850     compileBRe : /(\r\n|\n)/g,
851     compileCRe : /'/g,
852
853     /**
854      * Returns an HTML fragment of this template with the specified values applied.
855      * @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'})
856      * @return {String} The HTML fragment
857      * @hide repeat doc
858      */
859     applyTemplate : function(values){
860         var me = this,
861             useF = me.disableFormats !== true,
862             fm = Ext.util.Format,
863             tpl = me;
864
865         if(me.compiled){
866             return me.compiled(values);
867         }
868         function fn(m, name, format, args){
869             if (format && useF) {
870                 if (format.substr(0, 5) == "this.") {
871                     return tpl.call(format.substr(5), values[name], values);
872                 } else {
873                     if (args) {
874                         // quoted values are required for strings in compiled templates,
875                         // but for non compiled we need to strip them
876                         // quoted reversed for jsmin
877                         var re = me.argsRe;
878                         args = args.split(',');
879                         for(var i = 0, len = args.length; i < len; i++){
880                             args[i] = args[i].replace(re, "$1");
881                         }
882                         args = [values[name]].concat(args);
883                     } else {
884                         args = [values[name]];
885                     }
886                     return fm[format].apply(fm, args);
887                 }
888             } else {
889                 return values[name] !== undefined ? values[name] : "";
890             }
891         }
892         return me.html.replace(me.re, fn);
893     },
894
895     /**
896      * Compiles the template into an internal function, eliminating the RegEx overhead.
897      * @return {Ext.Template} this
898      * @hide repeat doc
899      */
900     compile : function(){
901         var me = this,
902             fm = Ext.util.Format,
903             useF = me.disableFormats !== true,
904             sep = Ext.isGecko ? "+" : ",",
905             body;
906
907         function fn(m, name, format, args){
908             if(format && useF){
909                 args = args ? ',' + args : "";
910                 if(format.substr(0, 5) != "this."){
911                     format = "fm." + format + '(';
912                 }else{
913                     format = 'this.call("'+ format.substr(5) + '", ';
914                     args = ", values";
915                 }
916             }else{
917                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
918             }
919             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
920         }
921
922         // branched to use + in gecko and [].join() in others
923         if(Ext.isGecko){
924             body = "this.compiled = function(values){ return '" +
925                    me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
926                     "';};";
927         }else{
928             body = ["this.compiled = function(values){ return ['"];
929             body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
930             body.push("'].join('');};");
931             body = body.join('');
932         }
933         eval(body);
934         return me;
935     },
936
937     // private function used to call members
938     call : function(fnName, value, allValues){
939         return this[fnName](value, allValues);
940     }
941 });
942 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
943 /*
944  * This is code is also distributed under MIT license for use
945  * with jQuery and prototype JavaScript libraries.
946  */
947 /**
948  * @class Ext.DomQuery
949 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).
950 <p>
951 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>
952
953 <p>
954 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.
955 </p>
956 <h4>Element Selectors:</h4>
957 <ul class="list">
958     <li> <b>*</b> any element</li>
959     <li> <b>E</b> an element with the tag E</li>
960     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
961     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
962     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
963     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
964 </ul>
965 <h4>Attribute Selectors:</h4>
966 <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
967 <ul class="list">
968     <li> <b>E[foo]</b> has an attribute "foo"</li>
969     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
970     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
971     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
972     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
973     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
974     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
975 </ul>
976 <h4>Pseudo Classes:</h4>
977 <ul class="list">
978     <li> <b>E:first-child</b> E is the first child of its parent</li>
979     <li> <b>E:last-child</b> E is the last child of its parent</li>
980     <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>
981     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
982     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
983     <li> <b>E:only-child</b> E is the only child of its parent</li>
984     <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>
985     <li> <b>E:first</b> the first E in the resultset</li>
986     <li> <b>E:last</b> the last E in the resultset</li>
987     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
988     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
989     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
990     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
991     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
992     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
993     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
994     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
995     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
996     <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
997 </ul>
998 <h4>CSS Value Selectors:</h4>
999 <ul class="list">
1000     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
1001     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
1002     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
1003     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
1004     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
1005     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
1006 </ul>
1007  * @singleton
1008  */
1009 Ext.DomQuery = function(){
1010     var cache = {}, 
1011         simpleCache = {}, 
1012         valueCache = {},
1013         nonSpace = /\S/,
1014         trimRe = /^\s+|\s+$/g,
1015         tplRe = /\{(\d+)\}/g,
1016         modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1017         tagTokenRe = /^(#)?([\w-\*]+)/,
1018         nthRe = /(\d*)n\+?(\d*)/, 
1019         nthRe2 = /\D/,
1020         // This is for IE MSXML which does not support expandos.
1021         // IE runs the same speed using setAttribute, however FF slows way down
1022         // and Safari completely fails so they need to continue to use expandos.
1023         isIE = window.ActiveXObject ? true : false,
1024         key = 30803;
1025     
1026     // this eval is stop the compressor from
1027     // renaming the variable to something shorter
1028     eval("var batch = 30803;");         
1029
1030     // Retrieve the child node from a particular
1031     // parent at the specified index.
1032     function child(parent, index){
1033         var i = 0,
1034             n = parent.firstChild;
1035         while(n){
1036             if(n.nodeType == 1){
1037                if(++i == index){
1038                    return n;
1039                }
1040             }
1041             n = n.nextSibling;
1042         }
1043         return null;
1044     }
1045
1046     // retrieve the next element node
1047     function next(n){   
1048         while((n = n.nextSibling) && n.nodeType != 1);
1049         return n;
1050     }
1051
1052     // retrieve the previous element node 
1053     function prev(n){
1054         while((n = n.previousSibling) && n.nodeType != 1);
1055         return n;
1056     }
1057
1058     // Mark each child node with a nodeIndex skipping and
1059     // removing empty text nodes.
1060     function children(parent){
1061         var n = parent.firstChild,
1062             nodeIndex = -1,
1063             nextNode;
1064         while(n){
1065             nextNode = n.nextSibling;
1066             // clean worthless empty nodes.
1067             if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1068                 parent.removeChild(n);
1069             }else{
1070                 // add an expando nodeIndex
1071                 n.nodeIndex = ++nodeIndex;
1072             }
1073             n = nextNode;
1074         }
1075         return this;
1076     }
1077
1078
1079     // nodeSet - array of nodes
1080     // cls - CSS Class
1081     function byClassName(nodeSet, cls){
1082         if(!cls){
1083             return nodeSet;
1084         }
1085         var result = [], ri = -1;
1086         for(var i = 0, ci; ci = nodeSet[i]; i++){
1087             if((' '+ci.className+' ').indexOf(cls) != -1){
1088                 result[++ri] = ci;
1089             }
1090         }
1091         return result;
1092     };
1093
1094     function attrValue(n, attr){
1095         // if its an array, use the first node.
1096         if(!n.tagName && typeof n.length != "undefined"){
1097             n = n[0];
1098         }
1099         if(!n){
1100             return null;
1101         }
1102
1103         if(attr == "for"){
1104             return n.htmlFor;
1105         }
1106         if(attr == "class" || attr == "className"){
1107             return n.className;
1108         }
1109         return n.getAttribute(attr) || n[attr];
1110
1111     };
1112
1113
1114     // ns - nodes
1115     // mode - false, /, >, +, ~
1116     // tagName - defaults to "*"
1117     function getNodes(ns, mode, tagName){
1118         var result = [], ri = -1, cs;
1119         if(!ns){
1120             return result;
1121         }
1122         tagName = tagName || "*";
1123         // convert to array
1124         if(typeof ns.getElementsByTagName != "undefined"){
1125             ns = [ns];
1126         }
1127         
1128         // no mode specified, grab all elements by tagName
1129         // at any depth
1130         if(!mode){
1131             for(var i = 0, ni; ni = ns[i]; i++){
1132                 cs = ni.getElementsByTagName(tagName);
1133                 for(var j = 0, ci; ci = cs[j]; j++){
1134                     result[++ri] = ci;
1135                 }
1136             }
1137         // Direct Child mode (/ or >)
1138         // E > F or E/F all direct children elements of E that have the tag     
1139         } else if(mode == "/" || mode == ">"){
1140             var utag = tagName.toUpperCase();
1141             for(var i = 0, ni, cn; ni = ns[i]; i++){
1142                 cn = ni.childNodes;
1143                 for(var j = 0, cj; cj = cn[j]; j++){
1144                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
1145                         result[++ri] = cj;
1146                     }
1147                 }
1148             }
1149         // Immediately Preceding mode (+)
1150         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1151         }else if(mode == "+"){
1152             var utag = tagName.toUpperCase();
1153             for(var i = 0, n; n = ns[i]; i++){
1154                 while((n = n.nextSibling) && n.nodeType != 1);
1155                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1156                     result[++ri] = n;
1157                 }
1158             }
1159         // Sibling mode (~)
1160         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1161         }else if(mode == "~"){
1162             var utag = tagName.toUpperCase();
1163             for(var i = 0, n; n = ns[i]; i++){
1164                 while((n = n.nextSibling)){
1165                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1166                         result[++ri] = n;
1167                     }
1168                 }
1169             }
1170         }
1171         return result;
1172     }
1173
1174     function concat(a, b){
1175         if(b.slice){
1176             return a.concat(b);
1177         }
1178         for(var i = 0, l = b.length; i < l; i++){
1179             a[a.length] = b[i];
1180         }
1181         return a;
1182     }
1183
1184     function byTag(cs, tagName){
1185         if(cs.tagName || cs == document){
1186             cs = [cs];
1187         }
1188         if(!tagName){
1189             return cs;
1190         }
1191         var result = [], ri = -1;
1192         tagName = tagName.toLowerCase();
1193         for(var i = 0, ci; ci = cs[i]; i++){
1194             if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1195                 result[++ri] = ci;
1196             }
1197         }
1198         return result;
1199     }
1200
1201     function byId(cs, id){
1202         if(cs.tagName || cs == document){
1203             cs = [cs];
1204         }
1205         if(!id){
1206             return cs;
1207         }
1208         var result = [], ri = -1;
1209         for(var i = 0, ci; ci = cs[i]; i++){
1210             if(ci && ci.id == id){
1211                 result[++ri] = ci;
1212                 return result;
1213             }
1214         }
1215         return result;
1216     }
1217
1218     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1219     // custom can be "{"
1220     function byAttribute(cs, attr, value, op, custom){
1221         var result = [], 
1222             ri = -1, 
1223             useGetStyle = custom == "{",            
1224             fn = Ext.DomQuery.operators[op],        
1225             a,      
1226             innerHTML;
1227         for(var i = 0, ci; ci = cs[i]; i++){
1228             // skip non-element nodes.
1229             if(ci.nodeType != 1){
1230                 continue;
1231             }
1232             
1233             innerHTML = ci.innerHTML;
1234             // we only need to change the property names if we're dealing with html nodes, not XML
1235             if(innerHTML !== null && innerHTML !== undefined){
1236                 if(useGetStyle){
1237                     a = Ext.DomQuery.getStyle(ci, attr);
1238                 } else if (attr == "class" || attr == "className"){
1239                     a = ci.className;
1240                 } else if (attr == "for"){
1241                     a = ci.htmlFor;
1242                 } else if (attr == "href"){
1243                     // getAttribute href bug
1244                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1245                     a = ci.getAttribute("href", 2);
1246                 } else{
1247                     a = ci.getAttribute(attr);
1248                 }
1249             }else{
1250                 a = ci.getAttribute(attr);
1251             }
1252             if((fn && fn(a, value)) || (!fn && a)){
1253                 result[++ri] = ci;
1254             }
1255         }
1256         return result;
1257     }
1258
1259     function byPseudo(cs, name, value){
1260         return Ext.DomQuery.pseudos[name](cs, value);
1261     }
1262
1263     function nodupIEXml(cs){
1264         var d = ++key, 
1265             r;
1266         cs[0].setAttribute("_nodup", d);
1267         r = [cs[0]];
1268         for(var i = 1, len = cs.length; i < len; i++){
1269             var c = cs[i];
1270             if(!c.getAttribute("_nodup") != d){
1271                 c.setAttribute("_nodup", d);
1272                 r[r.length] = c;
1273             }
1274         }
1275         for(var i = 0, len = cs.length; i < len; i++){
1276             cs[i].removeAttribute("_nodup");
1277         }
1278         return r;
1279     }
1280
1281     function nodup(cs){
1282         if(!cs){
1283             return [];
1284         }
1285         var len = cs.length, c, i, r = cs, cj, ri = -1;
1286         if(!len || typeof cs.nodeType != "undefined" || len == 1){
1287             return cs;
1288         }
1289         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1290             return nodupIEXml(cs);
1291         }
1292         var d = ++key;
1293         cs[0]._nodup = d;
1294         for(i = 1; c = cs[i]; i++){
1295             if(c._nodup != d){
1296                 c._nodup = d;
1297             }else{
1298                 r = [];
1299                 for(var j = 0; j < i; j++){
1300                     r[++ri] = cs[j];
1301                 }
1302                 for(j = i+1; cj = cs[j]; j++){
1303                     if(cj._nodup != d){
1304                         cj._nodup = d;
1305                         r[++ri] = cj;
1306                     }
1307                 }
1308                 return r;
1309             }
1310         }
1311         return r;
1312     }
1313
1314     function quickDiffIEXml(c1, c2){
1315         var d = ++key,
1316             r = [];
1317         for(var i = 0, len = c1.length; i < len; i++){
1318             c1[i].setAttribute("_qdiff", d);
1319         }        
1320         for(var i = 0, len = c2.length; i < len; i++){
1321             if(c2[i].getAttribute("_qdiff") != d){
1322                 r[r.length] = c2[i];
1323             }
1324         }
1325         for(var i = 0, len = c1.length; i < len; i++){
1326            c1[i].removeAttribute("_qdiff");
1327         }
1328         return r;
1329     }
1330
1331     function quickDiff(c1, c2){
1332         var len1 = c1.length,
1333                 d = ++key,
1334                 r = [];
1335         if(!len1){
1336             return c2;
1337         }
1338         if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1339             return quickDiffIEXml(c1, c2);
1340         }        
1341         for(var i = 0; i < len1; i++){
1342             c1[i]._qdiff = d;
1343         }        
1344         for(var i = 0, len = c2.length; i < len; i++){
1345             if(c2[i]._qdiff != d){
1346                 r[r.length] = c2[i];
1347             }
1348         }
1349         return r;
1350     }
1351
1352     function quickId(ns, mode, root, id){
1353         if(ns == root){
1354            var d = root.ownerDocument || root;
1355            return d.getElementById(id);
1356         }
1357         ns = getNodes(ns, mode, "*");
1358         return byId(ns, id);
1359     }
1360
1361     return {
1362         getStyle : function(el, name){
1363             return Ext.fly(el).getStyle(name);
1364         },
1365         /**
1366          * Compiles a selector/xpath query into a reusable function. The returned function
1367          * takes one parameter "root" (optional), which is the context node from where the query should start.
1368          * @param {String} selector The selector/xpath query
1369          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1370          * @return {Function}
1371          */
1372         compile : function(path, type){
1373             type = type || "select";
1374
1375             // setup fn preamble
1376             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1377                 mode,           
1378                 lastPath,
1379                 matchers = Ext.DomQuery.matchers,
1380                 matchersLn = matchers.length,
1381                 modeMatch,
1382                 // accept leading mode switch
1383                 lmode = path.match(modeRe);
1384             
1385             if(lmode && lmode[1]){
1386                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1387                 path = path.replace(lmode[1], "");
1388             }
1389             
1390             // strip leading slashes
1391             while(path.substr(0, 1)=="/"){
1392                 path = path.substr(1);
1393             }
1394
1395             while(path && lastPath != path){
1396                 lastPath = path;
1397                 var tokenMatch = path.match(tagTokenRe);
1398                 if(type == "select"){
1399                     if(tokenMatch){
1400                         // ID Selector
1401                         if(tokenMatch[1] == "#"){
1402                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';                 
1403                         }else{
1404                             fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1405                         }
1406                         path = path.replace(tokenMatch[0], "");
1407                     }else if(path.substr(0, 1) != '@'){
1408                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
1409                     }
1410                 // type of "simple"
1411                 }else{
1412                     if(tokenMatch){
1413                         if(tokenMatch[1] == "#"){
1414                             fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1415                         }else{
1416                             fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1417                         }
1418                         path = path.replace(tokenMatch[0], "");
1419                     }
1420                 }
1421                 while(!(modeMatch = path.match(modeRe))){
1422                     var matched = false;
1423                     for(var j = 0; j < matchersLn; j++){
1424                         var t = matchers[j];
1425                         var m = path.match(t.re);
1426                         if(m){
1427                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
1428                                 return m[i];
1429                             });
1430                             path = path.replace(m[0], "");
1431                             matched = true;
1432                             break;
1433                         }
1434                     }
1435                     // prevent infinite loop on bad selector
1436                     if(!matched){
1437                         throw 'Error parsing selector, parsing failed at "' + path + '"';
1438                     }
1439                 }
1440                 if(modeMatch[1]){
1441                     fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1442                     path = path.replace(modeMatch[1], "");
1443                 }
1444             }
1445             // close fn out
1446             fn[fn.length] = "return nodup(n);\n}";
1447             
1448             // eval fn and return it
1449             eval(fn.join(""));
1450             return f;
1451         },
1452
1453         /**
1454          * Selects a group of elements.
1455          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1456          * @param {Node/String} root (optional) The start of the query (defaults to document).
1457          * @return {Array} An Array of DOM elements which match the selector. If there are
1458          * no matches, and empty Array is returned.
1459          */
1460         jsSelect: function(path, root, type){
1461             // set root to doc if not specified.
1462             root = root || document;
1463             
1464             if(typeof root == "string"){
1465                 root = document.getElementById(root);
1466             }
1467             var paths = path.split(","),
1468                 results = [];
1469                 
1470             // loop over each selector
1471             for(var i = 0, len = paths.length; i < len; i++){           
1472                 var subPath = paths[i].replace(trimRe, "");
1473                 // compile and place in cache
1474                 if(!cache[subPath]){
1475                     cache[subPath] = Ext.DomQuery.compile(subPath);
1476                     if(!cache[subPath]){
1477                         throw subPath + " is not a valid selector";
1478                     }
1479                 }
1480                 var result = cache[subPath](root);
1481                 if(result && result != document){
1482                     results = results.concat(result);
1483                 }
1484             }
1485             
1486             // if there were multiple selectors, make sure dups
1487             // are eliminated
1488             if(paths.length > 1){
1489                 return nodup(results);
1490             }
1491             return results;
1492         },
1493         isXml: function(el) {
1494             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1495             return docEl ? docEl.nodeName !== "HTML" : false;
1496         },
1497         select : document.querySelectorAll ? function(path, root, type) {
1498             root = root || document;
1499             if (!Ext.DomQuery.isXml(root)) {
1500                 try {
1501                     var cs = root.querySelectorAll(path);
1502                     return Ext.toArray(cs);
1503                 }
1504                 catch (ex) {}           
1505             }       
1506             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1507         } : function(path, root, type) {
1508             return Ext.DomQuery.jsSelect.call(this, path, root, type);
1509         },
1510
1511         /**
1512          * Selects a single element.
1513          * @param {String} selector The selector/xpath query
1514          * @param {Node} root (optional) The start of the query (defaults to document).
1515          * @return {Element} The DOM element which matched the selector.
1516          */
1517         selectNode : function(path, root){
1518             return Ext.DomQuery.select(path, root)[0];
1519         },
1520
1521         /**
1522          * Selects the value of a node, optionally replacing null with the defaultValue.
1523          * @param {String} selector The selector/xpath query
1524          * @param {Node} root (optional) The start of the query (defaults to document).
1525          * @param {String} defaultValue
1526          * @return {String}
1527          */
1528         selectValue : function(path, root, defaultValue){
1529             path = path.replace(trimRe, "");
1530             if(!valueCache[path]){
1531                 valueCache[path] = Ext.DomQuery.compile(path, "select");
1532             }
1533             var n = valueCache[path](root), v;
1534             n = n[0] ? n[0] : n;
1535                     
1536             // overcome a limitation of maximum textnode size
1537             // Rumored to potentially crash IE6 but has not been confirmed.
1538             // http://reference.sitepoint.com/javascript/Node/normalize
1539             // https://developer.mozilla.org/En/DOM/Node.normalize          
1540             if (typeof n.normalize == 'function') n.normalize();
1541             
1542             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1543             return ((v === null||v === undefined||v==='') ? defaultValue : v);
1544         },
1545
1546         /**
1547          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1548          * @param {String} selector The selector/xpath query
1549          * @param {Node} root (optional) The start of the query (defaults to document).
1550          * @param {Number} defaultValue
1551          * @return {Number}
1552          */
1553         selectNumber : function(path, root, defaultValue){
1554             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1555             return parseFloat(v);
1556         },
1557
1558         /**
1559          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1560          * @param {String/HTMLElement/Array} el An element id, element or array of elements
1561          * @param {String} selector The simple selector to test
1562          * @return {Boolean}
1563          */
1564         is : function(el, ss){
1565             if(typeof el == "string"){
1566                 el = document.getElementById(el);
1567             }
1568             var isArray = Ext.isArray(el),
1569                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1570             return isArray ? (result.length == el.length) : (result.length > 0);
1571         },
1572
1573         /**
1574          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1575          * @param {Array} el An array of elements to filter
1576          * @param {String} selector The simple selector to test
1577          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1578          * the selector instead of the ones that match
1579          * @return {Array} An Array of DOM elements which match the selector. If there are
1580          * no matches, and empty Array is returned.
1581          */
1582         filter : function(els, ss, nonMatches){
1583             ss = ss.replace(trimRe, "");
1584             if(!simpleCache[ss]){
1585                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1586             }
1587             var result = simpleCache[ss](els);
1588             return nonMatches ? quickDiff(result, els) : result;
1589         },
1590
1591         /**
1592          * Collection of matching regular expressions and code snippets.
1593          * Each capture group within () will be replace the {} in the select
1594          * statement as specified by their index.
1595          */
1596         matchers : [{
1597                 re: /^\.([\w-]+)/,
1598                 select: 'n = byClassName(n, " {1} ");'
1599             }, {
1600                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1601                 select: 'n = byPseudo(n, "{1}", "{2}");'
1602             },{
1603                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1604                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1605             }, {
1606                 re: /^#([\w-]+)/,
1607                 select: 'n = byId(n, "{1}");'
1608             },{
1609                 re: /^@([\w-]+)/,
1610                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1611             }
1612         ],
1613
1614         /**
1615          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1616          * 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;.
1617          */
1618         operators : {
1619             "=" : function(a, v){
1620                 return a == v;
1621             },
1622             "!=" : function(a, v){
1623                 return a != v;
1624             },
1625             "^=" : function(a, v){
1626                 return a && a.substr(0, v.length) == v;
1627             },
1628             "$=" : function(a, v){
1629                 return a && a.substr(a.length-v.length) == v;
1630             },
1631             "*=" : function(a, v){
1632                 return a && a.indexOf(v) !== -1;
1633             },
1634             "%=" : function(a, v){
1635                 return (a % v) == 0;
1636             },
1637             "|=" : function(a, v){
1638                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1639             },
1640             "~=" : function(a, v){
1641                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1642             }
1643         },
1644
1645         /**
1646          * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1647          * two parameters:</p><div class="mdetail-params"><ul>
1648          * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1649          * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1650          * </ul></div>
1651          * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1652          * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1653          * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1654          * <p>For example, to filter <code>&lt;a></code> elements to only return links to <i>external</i> resources:</p>
1655          * <code><pre>
1656 Ext.DomQuery.pseudos.external = function(c, v){
1657     var r = [], ri = -1;
1658     for(var i = 0, ci; ci = c[i]; i++){
1659 //      Include in result set only if it's a link to an external resource
1660         if(ci.hostname != location.hostname){
1661             r[++ri] = ci;
1662         }
1663     }
1664     return r;
1665 };</pre></code>
1666          * Then external links could be gathered with the following statement:<code><pre>
1667 var externalLinks = Ext.select("a:external");
1668 </code></pre>
1669          */
1670         pseudos : {
1671             "first-child" : function(c){
1672                 var r = [], ri = -1, n;
1673                 for(var i = 0, ci; ci = n = c[i]; i++){
1674                     while((n = n.previousSibling) && n.nodeType != 1);
1675                     if(!n){
1676                         r[++ri] = ci;
1677                     }
1678                 }
1679                 return r;
1680             },
1681
1682             "last-child" : function(c){
1683                 var r = [], ri = -1, n;
1684                 for(var i = 0, ci; ci = n = c[i]; i++){
1685                     while((n = n.nextSibling) && n.nodeType != 1);
1686                     if(!n){
1687                         r[++ri] = ci;
1688                     }
1689                 }
1690                 return r;
1691             },
1692
1693             "nth-child" : function(c, a) {
1694                 var r = [], ri = -1,
1695                         m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1696                         f = (m[1] || 1) - 0, l = m[2] - 0;
1697                 for(var i = 0, n; n = c[i]; i++){
1698                     var pn = n.parentNode;
1699                     if (batch != pn._batch) {
1700                         var j = 0;
1701                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1702                             if(cn.nodeType == 1){
1703                                cn.nodeIndex = ++j;
1704                             }
1705                         }
1706                         pn._batch = batch;
1707                     }
1708                     if (f == 1) {
1709                         if (l == 0 || n.nodeIndex == l){
1710                             r[++ri] = n;
1711                         }
1712                     } else if ((n.nodeIndex + l) % f == 0){
1713                         r[++ri] = n;
1714                     }
1715                 }
1716
1717                 return r;
1718             },
1719
1720             "only-child" : function(c){
1721                 var r = [], ri = -1;;
1722                 for(var i = 0, ci; ci = c[i]; i++){
1723                     if(!prev(ci) && !next(ci)){
1724                         r[++ri] = ci;
1725                     }
1726                 }
1727                 return r;
1728             },
1729
1730             "empty" : function(c){
1731                 var r = [], ri = -1;
1732                 for(var i = 0, ci; ci = c[i]; i++){
1733                     var cns = ci.childNodes, j = 0, cn, empty = true;
1734                     while(cn = cns[j]){
1735                         ++j;
1736                         if(cn.nodeType == 1 || cn.nodeType == 3){
1737                             empty = false;
1738                             break;
1739                         }
1740                     }
1741                     if(empty){
1742                         r[++ri] = ci;
1743                     }
1744                 }
1745                 return r;
1746             },
1747
1748             "contains" : function(c, v){
1749                 var r = [], ri = -1;
1750                 for(var i = 0, ci; ci = c[i]; i++){
1751                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1752                         r[++ri] = ci;
1753                     }
1754                 }
1755                 return r;
1756             },
1757
1758             "nodeValue" : function(c, v){
1759                 var r = [], ri = -1;
1760                 for(var i = 0, ci; ci = c[i]; i++){
1761                     if(ci.firstChild && ci.firstChild.nodeValue == v){
1762                         r[++ri] = ci;
1763                     }
1764                 }
1765                 return r;
1766             },
1767
1768             "checked" : function(c){
1769                 var r = [], ri = -1;
1770                 for(var i = 0, ci; ci = c[i]; i++){
1771                     if(ci.checked == true){
1772                         r[++ri] = ci;
1773                     }
1774                 }
1775                 return r;
1776             },
1777
1778             "not" : function(c, ss){
1779                 return Ext.DomQuery.filter(c, ss, true);
1780             },
1781
1782             "any" : function(c, selectors){
1783                 var ss = selectors.split('|'),
1784                         r = [], ri = -1, s;
1785                 for(var i = 0, ci; ci = c[i]; i++){
1786                     for(var j = 0; s = ss[j]; j++){
1787                         if(Ext.DomQuery.is(ci, s)){
1788                             r[++ri] = ci;
1789                             break;
1790                         }
1791                     }
1792                 }
1793                 return r;
1794             },
1795
1796             "odd" : function(c){
1797                 return this["nth-child"](c, "odd");
1798             },
1799
1800             "even" : function(c){
1801                 return this["nth-child"](c, "even");
1802             },
1803
1804             "nth" : function(c, a){
1805                 return c[a-1] || [];
1806             },
1807
1808             "first" : function(c){
1809                 return c[0] || [];
1810             },
1811
1812             "last" : function(c){
1813                 return c[c.length-1] || [];
1814             },
1815
1816             "has" : function(c, ss){
1817                 var s = Ext.DomQuery.select,
1818                         r = [], ri = -1;
1819                 for(var i = 0, ci; ci = c[i]; i++){
1820                     if(s(ss, ci).length > 0){
1821                         r[++ri] = ci;
1822                     }
1823                 }
1824                 return r;
1825             },
1826
1827             "next" : function(c, ss){
1828                 var is = Ext.DomQuery.is,
1829                         r = [], ri = -1;
1830                 for(var i = 0, ci; ci = c[i]; i++){
1831                     var n = next(ci);
1832                     if(n && is(n, ss)){
1833                         r[++ri] = ci;
1834                     }
1835                 }
1836                 return r;
1837             },
1838
1839             "prev" : function(c, ss){
1840                 var is = Ext.DomQuery.is,
1841                         r = [], ri = -1;
1842                 for(var i = 0, ci; ci = c[i]; i++){
1843                     var n = prev(ci);
1844                     if(n && is(n, ss)){
1845                         r[++ri] = ci;
1846                     }
1847                 }
1848                 return r;
1849             }
1850         }
1851     };
1852 }();
1853
1854 /**
1855  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1856  * @param {String} path The selector/xpath query
1857  * @param {Node} root (optional) The start of the query (defaults to document).
1858  * @return {Array}
1859  * @member Ext
1860  * @method query
1861  */
1862 Ext.query = Ext.DomQuery.select;
1863 /**
1864  * @class Ext.util.DelayedTask
1865  * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1866  * performing setTimeout where a new timeout cancels the old timeout. When called, the
1867  * task will wait the specified time period before executing. If durng that time period,
1868  * the task is called again, the original call will be cancelled. This continues so that
1869  * the function is only called a single time for each iteration.</p>
1870  * <p>This method is especially useful for things like detecting whether a user has finished
1871  * typing in a text field. An example would be performing validation on a keypress. You can
1872  * use this class to buffer the keypress events for a certain number of milliseconds, and
1873  * perform only if they stop for that amount of time.  Usage:</p><pre><code>
1874 var task = new Ext.util.DelayedTask(function(){
1875     alert(Ext.getDom('myInputField').value.length);
1876 });
1877 // Wait 500ms before calling our function. If the user presses another key 
1878 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1879 Ext.get('myInputField').on('keypress', function(){
1880     task.{@link #delay}(500); 
1881 });
1882  * </code></pre> 
1883  * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1884  * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1885  * also setup a delayed task for you to buffer events.</p> 
1886  * @constructor The parameters to this constructor serve as defaults and are not required.
1887  * @param {Function} fn (optional) The default function to call.
1888  * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1889  * function is called. If not specified, <code>this</code> will refer to the browser window.
1890  * @param {Array} args (optional) The default Array of arguments.
1891  */
1892 Ext.util.DelayedTask = function(fn, scope, args){
1893     var me = this,
1894         id,     
1895         call = function(){
1896                 clearInterval(id);
1897                 id = null;
1898                 fn.apply(scope, args || []);
1899             };
1900             
1901     /**
1902      * Cancels any pending timeout and queues a new one
1903      * @param {Number} delay The milliseconds to delay
1904      * @param {Function} newFn (optional) Overrides function passed to constructor
1905      * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1906      * is specified, <code>this</code> will refer to the browser window.
1907      * @param {Array} newArgs (optional) Overrides args passed to constructor
1908      */
1909     me.delay = function(delay, newFn, newScope, newArgs){
1910         me.cancel();
1911         fn = newFn || fn;
1912         scope = newScope || scope;
1913         args = newArgs || args;
1914         id = setInterval(call, delay);
1915     };
1916
1917     /**
1918      * Cancel the last queued timeout
1919      */
1920     me.cancel = function(){
1921         if(id){
1922             clearInterval(id);
1923             id = null;
1924         }
1925     };
1926 };(function(){
1927
1928 var EXTUTIL = Ext.util,
1929     EACH = Ext.each,
1930     TRUE = true,
1931     FALSE = false;
1932 /**
1933  * @class Ext.util.Observable
1934  * Base class that provides a common interface for publishing events. Subclasses are expected to
1935  * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1936  * with configured listeners defined.<br>
1937  * For example:
1938  * <pre><code>
1939 Employee = Ext.extend(Ext.util.Observable, {
1940     constructor: function(config){
1941         this.name = config.name;
1942         this.addEvents({
1943             "fired" : true,
1944             "quit" : true
1945         });
1946
1947         // Copy configured listeners into *this* object so that the base class&#39;s
1948         // constructor will add them.
1949         this.listeners = config.listeners;
1950
1951         // Call our superclass constructor to complete construction process.
1952         Employee.superclass.constructor.call(this, config)
1953     }
1954 });
1955 </code></pre>
1956  * This could then be used like this:<pre><code>
1957 var newEmployee = new Employee({
1958     name: employeeName,
1959     listeners: {
1960         quit: function() {
1961             // By default, "this" will be the object that fired the event.
1962             alert(this.name + " has quit!");
1963         }
1964     }
1965 });
1966 </code></pre>
1967  */
1968 EXTUTIL.Observable = function(){
1969     /**
1970      * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1971      * object during initialization.  This should be a valid listeners config object as specified in the
1972      * {@link #addListener} example for attaching multiple handlers at once.</p>
1973      * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1974      * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1975      * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1976      * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1977      * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1978      * has been rendered. A plugin can simplify this step:<pre><code>
1979 // Plugin is configured with a listeners config object.
1980 // The Component is appended to the argument list of all handler functions.
1981 Ext.DomObserver = Ext.extend(Object, {
1982     constructor: function(config) {
1983         this.listeners = config.listeners ? config.listeners : config;
1984     },
1985
1986     // Component passes itself into plugin&#39;s init method
1987     init: function(c) {
1988         var p, l = this.listeners;
1989         for (p in l) {
1990             if (Ext.isFunction(l[p])) {
1991                 l[p] = this.createHandler(l[p], c);
1992             } else {
1993                 l[p].fn = this.createHandler(l[p].fn, c);
1994             }
1995         }
1996
1997         // Add the listeners to the Element immediately following the render call
1998         c.render = c.render.{@link Function#createSequence createSequence}(function() {
1999             var e = c.getEl();
2000             if (e) {
2001                 e.on(l);
2002             }
2003         });
2004     },
2005
2006     createHandler: function(fn, c) {
2007         return function(e) {
2008             fn.call(this, e, c);
2009         };
2010     }
2011 });
2012
2013 var combo = new Ext.form.ComboBox({
2014
2015     // Collapse combo when its element is clicked on
2016     plugins: [ new Ext.DomObserver({
2017         click: function(evt, comp) {
2018             comp.collapse();
2019         }
2020     })],
2021     store: myStore,
2022     typeAhead: true,
2023     mode: 'local',
2024     triggerAction: 'all'
2025 });
2026      * </code></pre></p>
2027      */
2028     var me = this, e = me.events;
2029     if(me.listeners){
2030         me.on(me.listeners);
2031         delete me.listeners;
2032     }
2033     me.events = e || {};
2034 };
2035
2036 EXTUTIL.Observable.prototype = {
2037     // private
2038     filterOptRe : /^(?:scope|delay|buffer|single)$/,
2039
2040     /**
2041      * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2042      * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2043      * by calling {@link #enableBubble}.</p>
2044      * @param {String} eventName The name of the event to fire.
2045      * @param {Object...} args Variable number of parameters are passed to handlers.
2046      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2047      */
2048     fireEvent : function(){
2049         var a = Array.prototype.slice.call(arguments, 0),
2050             ename = a[0].toLowerCase(),
2051             me = this,
2052             ret = TRUE,
2053             ce = me.events[ename],
2054             cc,
2055             q,
2056             c;
2057         if (me.eventsSuspended === TRUE) {
2058             if (q = me.eventQueue) {
2059                 q.push(a);
2060             }
2061         }
2062         else if(typeof ce == 'object') {
2063             if (ce.bubble){
2064                 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2065                     return FALSE;
2066                 }
2067                 c = me.getBubbleTarget && me.getBubbleTarget();
2068                 if(c && c.enableBubble) {
2069                     cc = c.events[ename];
2070                     if(!cc || typeof cc != 'object' || !cc.bubble) {
2071                         c.enableBubble(ename);
2072                     }
2073                     return c.fireEvent.apply(c, a);
2074                 }
2075             }
2076             else {
2077                 a.shift();
2078                 ret = ce.fire.apply(ce, a);
2079             }
2080         }
2081         return ret;
2082     },
2083
2084     /**
2085      * Appends an event handler to this object.
2086      * @param {String}   eventName The name of the event to listen for.
2087      * @param {Function} handler The method the event invokes.
2088      * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2089      * <b>If omitted, defaults to the object which fired the event.</b>
2090      * @param {Object}   options (optional) An object containing handler configuration.
2091      * properties. This may contain any of the following properties:<ul>
2092      * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2093      * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2094      * <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>
2095      * <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>
2096      * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2097      * by the specified number of milliseconds. If the event fires again within that time, the original
2098      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2099      * <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>
2100      * if the event was bubbled up from a child Observable.</div></li>
2101      * </ul><br>
2102      * <p>
2103      * <b>Combining Options</b><br>
2104      * Using the options argument, it is possible to combine different types of listeners:<br>
2105      * <br>
2106      * A delayed, one-time listener.
2107      * <pre><code>
2108 myDataView.on('click', this.onClick, this, {
2109 single: true,
2110 delay: 100
2111 });</code></pre>
2112      * <p>
2113      * <b>Attaching multiple handlers in 1 call</b><br>
2114      * The method also allows for a single argument to be passed which is a config object containing properties
2115      * which specify multiple handlers.
2116      * <p>
2117      * <pre><code>
2118 myGridPanel.on({
2119 'click' : {
2120     fn: this.onClick,
2121     scope: this,
2122     delay: 100
2123 },
2124 'mouseover' : {
2125     fn: this.onMouseOver,
2126     scope: this
2127 },
2128 'mouseout' : {
2129     fn: this.onMouseOut,
2130     scope: this
2131 }
2132 });</code></pre>
2133  * <p>
2134  * Or a shorthand syntax:<br>
2135  * <pre><code>
2136 myGridPanel.on({
2137 'click' : this.onClick,
2138 'mouseover' : this.onMouseOver,
2139 'mouseout' : this.onMouseOut,
2140  scope: this
2141 });</code></pre>
2142      */
2143     addListener : function(eventName, fn, scope, o){
2144         var me = this,
2145             e,
2146             oe,
2147             isF,
2148         ce;
2149         if (typeof eventName == 'object') {
2150             o = eventName;
2151             for (e in o){
2152                 oe = o[e];
2153                 if (!me.filterOptRe.test(e)) {
2154                     me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2155                 }
2156             }
2157         } else {
2158             eventName = eventName.toLowerCase();
2159             ce = me.events[eventName] || TRUE;
2160             if (typeof ce == 'boolean') {
2161                 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2162             }
2163             ce.addListener(fn, scope, typeof o == 'object' ? o : {});
2164         }
2165     },
2166
2167     /**
2168      * Removes an event handler.
2169      * @param {String}   eventName The type of event the handler was associated with.
2170      * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2171      * @param {Object}   scope     (optional) The scope originally specified for the handler.
2172      */
2173     removeListener : function(eventName, fn, scope){
2174         var ce = this.events[eventName.toLowerCase()];
2175         if (typeof ce == 'object') {
2176             ce.removeListener(fn, scope);
2177         }
2178     },
2179
2180     /**
2181      * Removes all listeners for this object
2182      */
2183     purgeListeners : function(){
2184         var events = this.events,
2185             evt,
2186             key;
2187         for(key in events){
2188             evt = events[key];
2189             if(typeof evt == 'object'){
2190                 evt.clearListeners();
2191             }
2192         }
2193     },
2194
2195     /**
2196      * Adds the specified events to the list of events which this Observable may fire.
2197      * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2198      * or the first event name string if multiple event names are being passed as separate parameters.
2199      * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2200      * Usage:<pre><code>
2201 this.addEvents('storeloaded', 'storecleared');
2202 </code></pre>
2203      */
2204     addEvents : function(o){
2205         var me = this;
2206         me.events = me.events || {};
2207         if (typeof o == 'string') {
2208             var a = arguments,
2209                 i = a.length;
2210             while(i--) {
2211                 me.events[a[i]] = me.events[a[i]] || TRUE;
2212             }
2213         } else {
2214             Ext.applyIf(me.events, o);
2215         }
2216     },
2217
2218     /**
2219      * Checks to see if this object has any listeners for a specified event
2220      * @param {String} eventName The name of the event to check for
2221      * @return {Boolean} True if the event is being listened for, else false
2222      */
2223     hasListener : function(eventName){
2224         var e = this.events[eventName.toLowerCase()];
2225         return typeof e == 'object' && e.listeners.length > 0;
2226     },
2227
2228     /**
2229      * Suspend the firing of all events. (see {@link #resumeEvents})
2230      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2231      * after the {@link #resumeEvents} call instead of discarding all suspended events;
2232      */
2233     suspendEvents : function(queueSuspended){
2234         this.eventsSuspended = TRUE;
2235         if(queueSuspended && !this.eventQueue){
2236             this.eventQueue = [];
2237         }
2238     },
2239
2240     /**
2241      * Resume firing events. (see {@link #suspendEvents})
2242      * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2243      * events fired during event suspension will be sent to any listeners now.
2244      */
2245     resumeEvents : function(){
2246         var me = this,
2247             queued = me.eventQueue || [];
2248         me.eventsSuspended = FALSE;
2249         delete me.eventQueue;
2250         EACH(queued, function(e) {
2251             me.fireEvent.apply(me, e);
2252         });
2253     }
2254 };
2255
2256 var OBSERVABLE = EXTUTIL.Observable.prototype;
2257 /**
2258  * Appends an event handler to this object (shorthand for {@link #addListener}.)
2259  * @param {String}   eventName     The type of event to listen for
2260  * @param {Function} handler       The method the event invokes
2261  * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2262  * <b>If omitted, defaults to the object which fired the event.</b>
2263  * @param {Object}   options       (optional) An object containing handler configuration.
2264  * @method
2265  */
2266 OBSERVABLE.on = OBSERVABLE.addListener;
2267 /**
2268  * Removes an event handler (shorthand for {@link #removeListener}.)
2269  * @param {String}   eventName     The type of event the handler was associated with.
2270  * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2271  * @param {Object}   scope         (optional) The scope originally specified for the handler.
2272  * @method
2273  */
2274 OBSERVABLE.un = OBSERVABLE.removeListener;
2275
2276 /**
2277  * Removes <b>all</b> added captures from the Observable.
2278  * @param {Observable} o The Observable to release
2279  * @static
2280  */
2281 EXTUTIL.Observable.releaseCapture = function(o){
2282     o.fireEvent = OBSERVABLE.fireEvent;
2283 };
2284
2285 function createTargeted(h, o, scope){
2286     return function(){
2287         if(o.target == arguments[0]){
2288             h.apply(scope, Array.prototype.slice.call(arguments, 0));
2289         }
2290     };
2291 };
2292
2293 function createBuffered(h, o, l, scope){
2294     l.task = new EXTUTIL.DelayedTask();
2295     return function(){
2296         l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
2297     };
2298 };
2299
2300 function createSingle(h, e, fn, scope){
2301     return function(){
2302         e.removeListener(fn, scope);
2303         return h.apply(scope, arguments);
2304     };
2305 };
2306
2307 function createDelayed(h, o, l, scope){
2308     return function(){
2309         var task = new EXTUTIL.DelayedTask();
2310         if(!l.tasks) {
2311             l.tasks = [];
2312         }
2313         l.tasks.push(task);
2314         task.delay(o.delay || 10, h, scope, Array.prototype.slice.call(arguments, 0));
2315     };
2316 };
2317
2318 EXTUTIL.Event = function(obj, name){
2319     this.name = name;
2320     this.obj = obj;
2321     this.listeners = [];
2322 };
2323
2324 EXTUTIL.Event.prototype = {
2325     addListener : function(fn, scope, options){
2326         var me = this,
2327             l;
2328         scope = scope || me.obj;
2329         if(!me.isListening(fn, scope)){
2330             l = me.createListener(fn, scope, options);
2331             if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2332                 me.listeners = me.listeners.slice(0);
2333             }
2334             me.listeners.push(l);
2335         }
2336     },
2337
2338     createListener: function(fn, scope, o){
2339         o = o || {}, scope = scope || this.obj;
2340         var l = {
2341             fn: fn,
2342             scope: scope,
2343             options: o
2344         }, h = fn;
2345         if(o.target){
2346             h = createTargeted(h, o, scope);
2347         }
2348         if(o.delay){
2349             h = createDelayed(h, o, l, scope);
2350         }
2351         if(o.single){
2352             h = createSingle(h, this, fn, scope);
2353         }
2354         if(o.buffer){
2355             h = createBuffered(h, o, l, scope);
2356         }
2357         l.fireFn = h;
2358         return l;
2359     },
2360
2361     findListener : function(fn, scope){
2362         var list = this.listeners,
2363             i = list.length,
2364             l;
2365
2366         scope = scope || this.obj;
2367         while(i--){
2368             l = list[i];
2369             if(l){
2370                 if(l.fn == fn && l.scope == scope){
2371                     return i;
2372                 }
2373             }
2374         }
2375         return -1;
2376     },
2377
2378     isListening : function(fn, scope){
2379         return this.findListener(fn, scope) != -1;
2380     },
2381
2382     removeListener : function(fn, scope){
2383         var index,
2384             l,
2385             k,
2386             me = this,
2387             ret = FALSE;
2388         if((index = me.findListener(fn, scope)) != -1){
2389             if (me.firing) {
2390                 me.listeners = me.listeners.slice(0);
2391             }
2392             l = me.listeners[index];
2393             if(l.task) {
2394                 l.task.cancel();
2395                 delete l.task;
2396             }
2397             k = l.tasks && l.tasks.length;
2398             if(k) {
2399                 while(k--) {
2400                     l.tasks[k].cancel();
2401                 }
2402                 delete l.tasks;
2403             }
2404             me.listeners.splice(index, 1);
2405             ret = TRUE;
2406         }
2407         return ret;
2408     },
2409
2410     // Iterate to stop any buffered/delayed events
2411     clearListeners : function(){
2412         var me = this,
2413             l = me.listeners,
2414             i = l.length;
2415         while(i--) {
2416             me.removeListener(l[i].fn, l[i].scope);
2417         }
2418     },
2419
2420     fire : function(){
2421         var me = this,
2422             listeners = me.listeners,
2423             len = listeners.length,
2424             i = 0,
2425             l;
2426
2427         if(len > 0){
2428             me.firing = TRUE;
2429             var args = Array.prototype.slice.call(arguments, 0);
2430             for (; i < len; i++) {
2431                 l = listeners[i];
2432                 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2433                     return (me.firing = FALSE);
2434                 }
2435             }
2436         }
2437         me.firing = FALSE;
2438         return TRUE;
2439     }
2440
2441 };
2442 })();
2443 /**
2444  * @class Ext.util.Observable
2445  */
2446 Ext.apply(Ext.util.Observable.prototype, function(){
2447     // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2448     // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2449     // private
2450     function getMethodEvent(method){
2451         var e = (this.methodEvents = this.methodEvents ||
2452         {})[method], returnValue, v, cancel, obj = this;
2453
2454         if (!e) {
2455             this.methodEvents[method] = e = {};
2456             e.originalFn = this[method];
2457             e.methodName = method;
2458             e.before = [];
2459             e.after = [];
2460
2461             var makeCall = function(fn, scope, args){
2462                 if((v = fn.apply(scope || obj, args)) !== undefined){
2463                     if (typeof v == 'object') {
2464                         if(v.returnValue !== undefined){
2465                             returnValue = v.returnValue;
2466                         }else{
2467                             returnValue = v;
2468                         }
2469                         cancel = !!v.cancel;
2470                     }
2471                     else
2472                         if (v === false) {
2473                             cancel = true;
2474                         }
2475                         else {
2476                             returnValue = v;
2477                         }
2478                 }
2479             };
2480
2481             this[method] = function(){
2482                 var args = Array.prototype.slice.call(arguments, 0),
2483                     b;
2484                 returnValue = v = undefined;
2485                 cancel = false;
2486
2487                 for(var i = 0, len = e.before.length; i < len; i++){
2488                     b = e.before[i];
2489                     makeCall(b.fn, b.scope, args);
2490                     if (cancel) {
2491                         return returnValue;
2492                     }
2493                 }
2494
2495                 if((v = e.originalFn.apply(obj, args)) !== undefined){
2496                     returnValue = v;
2497                 }
2498
2499                 for(var i = 0, len = e.after.length; i < len; i++){
2500                     b = e.after[i];
2501                     makeCall(b.fn, b.scope, args);
2502                     if (cancel) {
2503                         return returnValue;
2504                     }
2505                 }
2506                 return returnValue;
2507             };
2508         }
2509         return e;
2510     }
2511
2512     return {
2513         // these are considered experimental
2514         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2515         // adds an 'interceptor' called before the original method
2516         beforeMethod : function(method, fn, scope){
2517             getMethodEvent.call(this, method).before.push({
2518                 fn: fn,
2519                 scope: scope
2520             });
2521         },
2522
2523         // adds a 'sequence' called after the original method
2524         afterMethod : function(method, fn, scope){
2525             getMethodEvent.call(this, method).after.push({
2526                 fn: fn,
2527                 scope: scope
2528             });
2529         },
2530
2531         removeMethodListener: function(method, fn, scope){
2532             var e = this.getMethodEvent(method);
2533             for(var i = 0, len = e.before.length; i < len; i++){
2534                 if(e.before[i].fn == fn && e.before[i].scope == scope){
2535                     e.before.splice(i, 1);
2536                     return;
2537                 }
2538             }
2539             for(var i = 0, len = e.after.length; i < len; i++){
2540                 if(e.after[i].fn == fn && e.after[i].scope == scope){
2541                     e.after.splice(i, 1);
2542                     return;
2543                 }
2544             }
2545         },
2546
2547         /**
2548          * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2549          * @param {Object} o The Observable whose events this object is to relay.
2550          * @param {Array} events Array of event names to relay.
2551          */
2552         relayEvents : function(o, events){
2553             var me = this;
2554             function createHandler(ename){
2555                 return function(){
2556                     return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
2557                 };
2558             }
2559             for(var i = 0, len = events.length; i < len; i++){
2560                 var ename = events[i];
2561                 me.events[ename] = me.events[ename] || true;
2562                 o.on(ename, createHandler(ename), me);
2563             }
2564         },
2565
2566         /**
2567          * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2568          * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2569          * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2570          * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2571          * access the required target more quickly.</p>
2572          * <p>Example:</p><pre><code>
2573 Ext.override(Ext.form.Field, {
2574     //  Add functionality to Field&#39;s initComponent to enable the change event to bubble
2575     initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2576         this.enableBubble('change');
2577     }),
2578
2579     //  We know that we want Field&#39;s events to bubble directly to the FormPanel.
2580     getBubbleTarget : function() {
2581         if (!this.formPanel) {
2582             this.formPanel = this.findParentByType('form');
2583         }
2584         return this.formPanel;
2585     }
2586 });
2587
2588 var myForm = new Ext.formPanel({
2589     title: 'User Details',
2590     items: [{
2591         ...
2592     }],
2593     listeners: {
2594         change: function() {
2595             // Title goes red if form has been modified.
2596             myForm.header.setStyle('color', 'red');
2597         }
2598     }
2599 });
2600 </code></pre>
2601          * @param {String/Array} events The event name to bubble, or an Array of event names.
2602          */
2603         enableBubble : function(events){
2604             var me = this;
2605             if(!Ext.isEmpty(events)){
2606                 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
2607                 for(var i = 0, len = events.length; i < len; i++){
2608                     var ename = events[i];
2609                     ename = ename.toLowerCase();
2610                     var ce = me.events[ename] || true;
2611                     if (typeof ce == 'boolean') {
2612                         ce = new Ext.util.Event(me, ename);
2613                         me.events[ename] = ce;
2614                     }
2615                     ce.bubble = true;
2616                 }
2617             }
2618         }
2619     };
2620 }());
2621
2622
2623 /**
2624  * Starts capture on the specified Observable. All events will be passed
2625  * to the supplied function with the event name + standard signature of the event
2626  * <b>before</b> the event is fired. If the supplied function returns false,
2627  * the event will not fire.
2628  * @param {Observable} o The Observable to capture events from.
2629  * @param {Function} fn The function to call when an event is fired.
2630  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2631  * @static
2632  */
2633 Ext.util.Observable.capture = function(o, fn, scope){
2634     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2635 };
2636
2637
2638 /**
2639  * Sets observability on the passed class constructor.<p>
2640  * <p>This makes any event fired on any instance of the passed class also fire a single event through
2641  * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2642  * <p>Usage:</p><pre><code>
2643 Ext.util.Observable.observeClass(Ext.data.Connection);
2644 Ext.data.Connection.on('beforerequest', function(con, options) {
2645     console.log('Ajax request made to ' + options.url);
2646 });</code></pre>
2647  * @param {Function} c The class constructor to make observable.
2648  * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
2649  * @static
2650  */
2651 Ext.util.Observable.observeClass = function(c, listeners){
2652     if(c){
2653       if(!c.fireEvent){
2654           Ext.apply(c, new Ext.util.Observable());
2655           Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2656       }
2657       if(typeof listeners == 'object'){
2658           c.on(listeners);
2659       }
2660       return c;
2661    }
2662 };
2663 /**
2664  * @class Ext.EventManager
2665  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2666  * several useful events directly.
2667  * See {@link Ext.EventObject} for more details on normalized event objects.
2668  * @singleton
2669  */
2670
2671 Ext.EventManager = function(){
2672     var docReadyEvent,
2673         docReadyProcId,
2674         docReadyState = false,
2675         DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2676         E = Ext.lib.Event,
2677         D = Ext.lib.Dom,
2678         DOC = document,
2679         WINDOW = window,
2680         DOMCONTENTLOADED = "DOMContentLoaded",
2681         COMPLETE = 'complete',
2682         propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2683         /*
2684          * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2685          * a reference to them so we can look them up at a later point.
2686          */
2687         specialElCache = [];
2688
2689      function getId(el){
2690         var id = false,
2691             i = 0,
2692             len = specialElCache.length,
2693             id = false,
2694             skip = false,
2695             o;
2696         if(el){
2697             if(el.getElementById || el.navigator){
2698                 // look up the id
2699                 for(; i < len; ++i){
2700                     o = specialElCache[i];
2701                     if(o.el === el){
2702                         id = o.id;
2703                         break;
2704                     }
2705                 }
2706                 if(!id){
2707                     // for browsers that support it, ensure that give the el the same id
2708                     id = Ext.id(el);
2709                     specialElCache.push({
2710                         id: id,
2711                         el: el
2712                     });
2713                     skip = true;
2714                 }
2715             }else{
2716                 id = Ext.id(el);
2717             }
2718             if(!Ext.elCache[id]){
2719                 Ext.Element.addToCache(new Ext.Element(el), id);
2720                 if(skip){
2721                     Ext.elCache[id].skipGC = true;
2722                 }
2723             }
2724         }
2725         return id;
2726      };
2727
2728     /// There is some jquery work around stuff here that isn't needed in Ext Core.
2729     function addListener(el, ename, fn, task, wrap, scope){
2730         el = Ext.getDom(el);
2731         var id = getId(el),
2732             es = Ext.elCache[id].events,
2733             wfn;
2734
2735         wfn = E.on(el, ename, wrap);
2736         es[ename] = es[ename] || [];
2737
2738         /* 0 = Original Function,
2739            1 = Event Manager Wrapped Function,
2740            2 = Scope,
2741            3 = Adapter Wrapped Function,
2742            4 = Buffered Task
2743         */
2744         es[ename].push([fn, wrap, scope, wfn, task]);
2745
2746         // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2747         // without breaking ExtJS.
2748
2749         // workaround for jQuery
2750         if(el.addEventListener && ename == "mousewheel"){
2751             var args = ["DOMMouseScroll", wrap, false];
2752             el.addEventListener.apply(el, args);
2753             Ext.EventManager.addListener(WINDOW, 'unload', function(){
2754                 el.removeEventListener.apply(el, args);
2755             });
2756         }
2757
2758         // fix stopped mousedowns on the document
2759         if(el == DOC && ename == "mousedown"){
2760             Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2761         }
2762     };
2763
2764     function doScrollChk(){
2765         /* Notes:
2766              'doScroll' will NOT work in a IFRAME/FRAMESET.
2767              The method succeeds but, a DOM query done immediately after -- FAILS.
2768           */
2769         if(window != top){
2770             return false;
2771         }
2772
2773         try{
2774             DOC.documentElement.doScroll('left');
2775         }catch(e){
2776              return false;
2777         }
2778
2779         fireDocReady();
2780         return true;
2781     }
2782     /**
2783      * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2784      * be true by other means). If false, the state is evaluated again until canceled.
2785      */
2786     function checkReadyState(e){
2787
2788         if(Ext.isIE && doScrollChk()){
2789             return true;
2790         }
2791         if(DOC.readyState == COMPLETE){
2792             fireDocReady();
2793             return true;
2794         }
2795         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2796         return false;
2797     }
2798
2799     var styles;
2800     function checkStyleSheets(e){
2801         styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2802         if(styles.length == DOC.styleSheets.length){
2803             fireDocReady();
2804             return true;
2805         }
2806         docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2807         return false;
2808     }
2809
2810     function OperaDOMContentLoaded(e){
2811         DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2812         checkStyleSheets();
2813     }
2814
2815     function fireDocReady(e){
2816         if(!docReadyState){
2817             docReadyState = true; //only attempt listener removal once
2818
2819             if(docReadyProcId){
2820                 clearTimeout(docReadyProcId);
2821             }
2822             if(DETECT_NATIVE) {
2823                 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2824             }
2825             if(Ext.isIE && checkReadyState.bindIE){  //was this was actually set ??
2826                 DOC.detachEvent('onreadystatechange', checkReadyState);
2827             }
2828             E.un(WINDOW, "load", arguments.callee);
2829         }
2830         if(docReadyEvent && !Ext.isReady){
2831             Ext.isReady = true;
2832             docReadyEvent.fire();
2833             docReadyEvent.listeners = [];
2834         }
2835
2836     };
2837
2838     function initDocReady(){
2839         docReadyEvent || (docReadyEvent = new Ext.util.Event());
2840         if (DETECT_NATIVE) {
2841             DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2842         }
2843         /*
2844          * Handle additional (exceptional) detection strategies here
2845          */
2846         if (Ext.isIE){
2847             //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2848             //See if page is already loaded
2849             if(!checkReadyState()){
2850                 checkReadyState.bindIE = true;
2851                 DOC.attachEvent('onreadystatechange', checkReadyState);
2852             }
2853
2854         }else if(Ext.isOpera ){
2855             /* Notes:
2856                Opera needs special treatment needed here because CSS rules are NOT QUITE
2857                available after DOMContentLoaded is raised.
2858             */
2859
2860             //See if page is already loaded and all styleSheets are in place
2861             (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2862                 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2863
2864         }else if (Ext.isWebKit){
2865             //Fallback for older Webkits without DOMCONTENTLOADED support
2866             checkReadyState();
2867         }
2868         // no matter what, make sure it fires on load
2869         E.on(WINDOW, "load", fireDocReady);
2870     };
2871
2872     function createTargeted(h, o){
2873         return function(){
2874             var args = Ext.toArray(arguments);
2875             if(o.target == Ext.EventObject.setEvent(args[0]).target){
2876                 h.apply(this, args);
2877             }
2878         };
2879     };
2880
2881     function createBuffered(h, o, task){
2882         return function(e){
2883             // create new event object impl so new events don't wipe out properties
2884             task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2885         };
2886     };
2887
2888     function createSingle(h, el, ename, fn, scope){
2889         return function(e){
2890             Ext.EventManager.removeListener(el, ename, fn, scope);
2891             h(e);
2892         };
2893     };
2894
2895     function createDelayed(h, o, fn){
2896         return function(e){
2897             var task = new Ext.util.DelayedTask(h);
2898             if(!fn.tasks) {
2899                 fn.tasks = [];
2900             }
2901             fn.tasks.push(task);
2902             task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2903         };
2904     };
2905
2906     function listen(element, ename, opt, fn, scope){
2907         var o = (!opt || typeof opt == "boolean") ? {} : opt,
2908             el = Ext.getDom(element), task;
2909
2910         fn = fn || o.fn;
2911         scope = scope || o.scope;
2912
2913         if(!el){
2914             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2915         }
2916         function h(e){
2917             // prevent errors while unload occurring
2918             if(!Ext){// !window[xname]){  ==> can't we do this?
2919                 return;
2920             }
2921             e = Ext.EventObject.setEvent(e);
2922             var t;
2923             if (o.delegate) {
2924                 if(!(t = e.getTarget(o.delegate, el))){
2925                     return;
2926                 }
2927             } else {
2928                 t = e.target;
2929             }
2930             if (o.stopEvent) {
2931                 e.stopEvent();
2932             }
2933             if (o.preventDefault) {
2934                e.preventDefault();
2935             }
2936             if (o.stopPropagation) {
2937                 e.stopPropagation();
2938             }
2939             if (o.normalized) {
2940                 e = e.browserEvent;
2941             }
2942
2943             fn.call(scope || el, e, t, o);
2944         };
2945         if(o.target){
2946             h = createTargeted(h, o);
2947         }
2948         if(o.delay){
2949             h = createDelayed(h, o, fn);
2950         }
2951         if(o.single){
2952             h = createSingle(h, el, ename, fn, scope);
2953         }
2954         if(o.buffer){
2955             task = new Ext.util.DelayedTask(h);
2956             h = createBuffered(h, o, task);
2957         }
2958
2959         addListener(el, ename, fn, task, h, scope);
2960         return h;
2961     };
2962
2963     var pub = {
2964         /**
2965          * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
2966          * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2967          * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2968          * @param {String} eventName The name of the event to listen for.
2969          * @param {Function} handler The handler function the event invokes. This function is passed
2970          * the following parameters:<ul>
2971          * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2972          * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2973          * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2974          * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2975          * </ul>
2976          * @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>.
2977          * @param {Object} options (optional) An object containing handler configuration properties.
2978          * This may contain any of the following properties:<ul>
2979          * <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>
2980          * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2981          * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2982          * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2983          * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2984          * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2985          * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2986          * <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>
2987          * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2988          * by the specified number of milliseconds. If the event fires again within that time, the original
2989          * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2990          * <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>
2991          * </ul><br>
2992          * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2993          */
2994         addListener : function(element, eventName, fn, scope, options){
2995             if(typeof eventName == 'object'){
2996                 var o = eventName, e, val;
2997                 for(e in o){
2998                     val = o[e];
2999                     if(!propRe.test(e)){
3000                         if(Ext.isFunction(val)){
3001                             // shared options
3002                             listen(element, e, o, val, o.scope);
3003                         }else{
3004                             // individual options
3005                             listen(element, e, val);
3006                         }
3007                     }
3008                 }
3009             } else {
3010                 listen(element, eventName, options, fn, scope);
3011             }
3012         },
3013
3014         /**
3015          * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
3016          * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
3017          * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3018          * @param {String} eventName The name of the event.
3019          * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
3020          * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3021          * then this must refer to the same object.
3022          */
3023         removeListener : function(el, eventName, fn, scope){
3024             el = Ext.getDom(el);
3025             var id = getId(el),
3026                 f = el && (Ext.elCache[id].events)[eventName] || [],
3027                 wrap, i, l, k, len, fnc;
3028
3029             for (i = 0, len = f.length; i < len; i++) {
3030
3031                 /* 0 = Original Function,
3032                    1 = Event Manager Wrapped Function,
3033                    2 = Scope,
3034                    3 = Adapter Wrapped Function,
3035                    4 = Buffered Task
3036                 */
3037                 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3038                     if(fnc[4]) {
3039                         fnc[4].cancel();
3040                     }
3041                     k = fn.tasks && fn.tasks.length;
3042                     if(k) {
3043                         while(k--) {
3044                             fn.tasks[k].cancel();
3045                         }
3046                         delete fn.tasks;
3047                     }
3048                     wrap = fnc[1];
3049                     E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3050
3051                     // jQuery workaround that should be removed from Ext Core
3052                     if(wrap && el.addEventListener && eventName == "mousewheel"){
3053                         el.removeEventListener("DOMMouseScroll", wrap, false);
3054                     }
3055
3056                     // fix stopped mousedowns on the document
3057                     if(wrap && el == DOC && eventName == "mousedown"){
3058                         Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3059                     }
3060
3061                     f.splice(i, 1);
3062                     if (f.length === 0) {
3063                         delete Ext.elCache[id].events[eventName];
3064                     }
3065                     for (k in Ext.elCache[id].events) {
3066                         return false;
3067                     }
3068                     Ext.elCache[id].events = {};
3069                     return false;
3070                 }
3071             }
3072         },
3073
3074         /**
3075          * Removes all event handers from an element.  Typically you will use {@link Ext.Element#removeAllListeners}
3076          * directly on an Element in favor of calling this version.
3077          * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3078          */
3079         removeAll : function(el){
3080             el = Ext.getDom(el);
3081             var id = getId(el),
3082                 ec = Ext.elCache[id] || {},
3083                 es = ec.events || {},
3084                 f, i, len, ename, fn, k, wrap;
3085
3086             for(ename in es){
3087                 if(es.hasOwnProperty(ename)){
3088                     f = es[ename];
3089                     /* 0 = Original Function,
3090                        1 = Event Manager Wrapped Function,
3091                        2 = Scope,
3092                        3 = Adapter Wrapped Function,
3093                        4 = Buffered Task
3094                     */
3095                     for (i = 0, len = f.length; i < len; i++) {
3096                         fn = f[i];
3097                         if(fn[4]) {
3098                             fn[4].cancel();
3099                         }
3100                         if(fn[0].tasks && (k = fn[0].tasks.length)) {
3101                             while(k--) {
3102                                 fn[0].tasks[k].cancel();
3103                             }
3104                             delete fn.tasks;
3105                         }
3106                         wrap =  fn[1];
3107                         E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3108
3109                         // jQuery workaround that should be removed from Ext Core
3110                         if(el.addEventListener && wrap && ename == "mousewheel"){
3111                             el.removeEventListener("DOMMouseScroll", wrap, false);
3112                         }
3113
3114                         // fix stopped mousedowns on the document
3115                         if(wrap && el == DOC &&  ename == "mousedown"){
3116                             Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3117                         }
3118                     }
3119                 }
3120             }
3121             if (Ext.elCache[id]) {
3122                 Ext.elCache[id].events = {};
3123             }
3124         },
3125
3126         getListeners : function(el, eventName) {
3127             el = Ext.getDom(el);
3128             var id = getId(el),
3129                 ec = Ext.elCache[id] || {},
3130                 es = ec.events || {},
3131                 results = [];
3132             if (es && es[eventName]) {
3133                 return es[eventName];
3134             } else {
3135                 return null;
3136             }
3137         },
3138
3139         purgeElement : function(el, recurse, eventName) {
3140             el = Ext.getDom(el);
3141             var id = getId(el),
3142                 ec = Ext.elCache[id] || {},
3143                 es = ec.events || {},
3144                 i, f, len;
3145             if (eventName) {
3146                 if (es && es.hasOwnProperty(eventName)) {
3147                     f = es[eventName];
3148                     for (i = 0, len = f.length; i < len; i++) {
3149                         Ext.EventManager.removeListener(el, eventName, f[i][0]);
3150                     }
3151                 }
3152             } else {
3153                 Ext.EventManager.removeAll(el);
3154             }
3155             if (recurse && el && el.childNodes) {
3156                 for (i = 0, len = el.childNodes.length; i < len; i++) {
3157                     Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3158                 }
3159             }
3160         },
3161
3162         _unload : function() {
3163             var el;
3164             for (el in Ext.elCache) {
3165                 Ext.EventManager.removeAll(el);
3166             }
3167             delete Ext.elCache;
3168             delete Ext.Element._flyweights;
3169
3170             // Abort any outstanding Ajax requests
3171             var c,
3172                 conn,
3173                 tid,
3174                 ajax = Ext.lib.Ajax;
3175             (typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {};
3176             for (tid in conn) {
3177                 c = conn[tid];
3178                 if (c) {
3179                     ajax.abort({conn: c, tId: tid});
3180                 }
3181             }
3182         },
3183         /**
3184          * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3185          * accessed shorthanded as Ext.onReady().
3186          * @param {Function} fn The method the event invokes.
3187          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3188          * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3189          * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3190          */
3191         onDocumentReady : function(fn, scope, options){
3192             if(Ext.isReady){ // if it already fired or document.body is present
3193                 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3194                 docReadyEvent.addListener(fn, scope, options);
3195                 docReadyEvent.fire();
3196                 docReadyEvent.listeners = [];
3197             }else{
3198                 if(!docReadyEvent){
3199                     initDocReady();
3200                 }
3201                 options = options || {};
3202                 options.delay = options.delay || 1;
3203                 docReadyEvent.addListener(fn, scope, options);
3204             }
3205         },
3206
3207         /**
3208          * Forces a document ready state transition for the framework.  Used when Ext is loaded
3209          * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3210          * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3211          */
3212         fireDocReady  : fireDocReady
3213     };
3214      /**
3215      * Appends an event handler to an element.  Shorthand for {@link #addListener}.
3216      * @param {String/HTMLElement} el The html element or id to assign the event handler to
3217      * @param {String} eventName The name of the event to listen for.
3218      * @param {Function} handler The handler function the event invokes.
3219      * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3220      * @param {Object} options (optional) An object containing standard {@link #addListener} options
3221      * @member Ext.EventManager
3222      * @method on
3223      */
3224     pub.on = pub.addListener;
3225     /**
3226      * Removes an event handler from an element.  Shorthand for {@link #removeListener}.
3227      * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3228      * @param {String} eventName The name of the event.
3229      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3230      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3231      * then this must refer to the same object.
3232      * @member Ext.EventManager
3233      * @method un
3234      */
3235     pub.un = pub.removeListener;
3236
3237     pub.stoppedMouseDownEvent = new Ext.util.Event();
3238     return pub;
3239 }();
3240 /**
3241   * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3242   * @param {Function} fn The method the event invokes.
3243   * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3244   * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3245   * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3246   * @member Ext
3247   * @method onReady
3248  */
3249 Ext.onReady = Ext.EventManager.onDocumentReady;
3250
3251
3252 //Initialize doc classes
3253 (function(){
3254
3255     var initExtCss = function(){
3256         // find the body element
3257         var bd = document.body || document.getElementsByTagName('body')[0];
3258         if(!bd){ return false; }
3259         var cls = [' ',
3260                 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3261                 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3262                 : Ext.isOpera ? "ext-opera"
3263                 : Ext.isWebKit ? "ext-webkit" : ""];
3264
3265         if(Ext.isSafari){
3266             cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3267         }else if(Ext.isChrome){
3268             cls.push("ext-chrome");
3269         }
3270
3271         if(Ext.isMac){
3272             cls.push("ext-mac");
3273         }
3274         if(Ext.isLinux){
3275             cls.push("ext-linux");
3276         }
3277
3278         if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3279             var p = bd.parentNode;
3280             if(p){
3281                 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3282             }
3283         }
3284         bd.className += cls.join(' ');
3285         return true;
3286     }
3287
3288     if(!initExtCss()){
3289         Ext.onReady(initExtCss);
3290     }
3291 })();
3292
3293
3294 /**
3295  * @class Ext.EventObject
3296  * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3297  * wraps the browser's native event-object normalizing cross-browser differences,
3298  * such as which mouse button is clicked, keys pressed, mechanisms to stop
3299  * event-propagation along with a method to prevent default actions from taking place.
3300  * <p>For example:</p>
3301  * <pre><code>
3302 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3303     e.preventDefault();
3304     var target = e.getTarget(); // same as t (the target HTMLElement)
3305     ...
3306 }
3307 var myDiv = {@link Ext#get Ext.get}("myDiv");  // get reference to an {@link Ext.Element}
3308 myDiv.on(         // 'on' is shorthand for addListener
3309     "click",      // perform an action on click of myDiv
3310     handleClick   // reference to the action handler
3311 );
3312 // other methods to do the same:
3313 Ext.EventManager.on("myDiv", 'click', handleClick);
3314 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3315  </code></pre>
3316  * @singleton
3317  */
3318 Ext.EventObject = function(){
3319     var E = Ext.lib.Event,
3320         // safari keypress events for special keys return bad keycodes
3321         safariKeys = {
3322             3 : 13, // enter
3323             63234 : 37, // left
3324             63235 : 39, // right
3325             63232 : 38, // up
3326             63233 : 40, // down
3327             63276 : 33, // page up
3328             63277 : 34, // page down
3329             63272 : 46, // delete
3330             63273 : 36, // home
3331             63275 : 35  // end
3332         },
3333         // normalize button clicks
3334         btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3335                 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3336
3337     Ext.EventObjectImpl = function(e){
3338         if(e){
3339             this.setEvent(e.browserEvent || e);
3340         }
3341     };
3342
3343     Ext.EventObjectImpl.prototype = {
3344            /** @private */
3345         setEvent : function(e){
3346             var me = this;
3347             if(e == me || (e && e.browserEvent)){ // already wrapped
3348                 return e;
3349             }
3350             me.browserEvent = e;
3351             if(e){
3352                 // normalize buttons
3353                 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3354                 if(e.type == 'click' && me.button == -1){
3355                     me.button = 0;
3356                 }
3357                 me.type = e.type;
3358                 me.shiftKey = e.shiftKey;
3359                 // mac metaKey behaves like ctrlKey
3360                 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3361                 me.altKey = e.altKey;
3362                 // in getKey these will be normalized for the mac
3363                 me.keyCode = e.keyCode;
3364                 me.charCode = e.charCode;
3365                 // cache the target for the delayed and or buffered events
3366                 me.target = E.getTarget(e);
3367                 // same for XY
3368                 me.xy = E.getXY(e);
3369             }else{
3370                 me.button = -1;
3371                 me.shiftKey = false;
3372                 me.ctrlKey = false;
3373                 me.altKey = false;
3374                 me.keyCode = 0;
3375                 me.charCode = 0;
3376                 me.target = null;
3377                 me.xy = [0, 0];
3378             }
3379             return me;
3380         },
3381
3382         /**
3383          * Stop the event (preventDefault and stopPropagation)
3384          */
3385         stopEvent : function(){
3386             var me = this;
3387             if(me.browserEvent){
3388                 if(me.browserEvent.type == 'mousedown'){
3389                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3390                 }
3391                 E.stopEvent(me.browserEvent);
3392             }
3393         },
3394
3395         /**
3396          * Prevents the browsers default handling of the event.
3397          */
3398         preventDefault : function(){
3399             if(this.browserEvent){
3400                 E.preventDefault(this.browserEvent);
3401             }
3402         },
3403
3404         /**
3405          * Cancels bubbling of the event.
3406          */
3407         stopPropagation : function(){
3408             var me = this;
3409             if(me.browserEvent){
3410                 if(me.browserEvent.type == 'mousedown'){
3411                     Ext.EventManager.stoppedMouseDownEvent.fire(me);
3412                 }
3413                 E.stopPropagation(me.browserEvent);
3414             }
3415         },
3416
3417         /**
3418          * Gets the character code for the event.
3419          * @return {Number}
3420          */
3421         getCharCode : function(){
3422             return this.charCode || this.keyCode;
3423         },
3424
3425         /**
3426          * Returns a normalized keyCode for the event.
3427          * @return {Number} The key code
3428          */
3429         getKey : function(){
3430             return this.normalizeKey(this.keyCode || this.charCode)
3431         },
3432
3433         // private
3434         normalizeKey: function(k){
3435             return Ext.isSafari ? (safariKeys[k] || k) : k;
3436         },
3437
3438         /**
3439          * Gets the x coordinate of the event.
3440          * @return {Number}
3441          */
3442         getPageX : function(){
3443             return this.xy[0];
3444         },
3445
3446         /**
3447          * Gets the y coordinate of the event.
3448          * @return {Number}
3449          */
3450         getPageY : function(){
3451             return this.xy[1];
3452         },
3453
3454         /**
3455          * Gets the page coordinates of the event.
3456          * @return {Array} The xy values like [x, y]
3457          */
3458         getXY : function(){
3459             return this.xy;
3460         },
3461
3462         /**
3463          * Gets the target for the event.
3464          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3465          * @param {Number/Mixed} maxDepth (optional) The max depth to
3466                 search as a number or element (defaults to 10 || document.body)
3467          * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3468          * @return {HTMLelement}
3469          */
3470         getTarget : function(selector, maxDepth, returnEl){
3471             return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3472         },
3473
3474         /**
3475          * Gets the related target.
3476          * @return {HTMLElement}
3477          */
3478         getRelatedTarget : function(){
3479             return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3480         },
3481
3482         /**
3483          * Normalizes mouse wheel delta across browsers
3484          * @return {Number} The delta
3485          */
3486         getWheelDelta : function(){
3487             var e = this.browserEvent;
3488             var delta = 0;
3489             if(e.wheelDelta){ /* IE/Opera. */
3490                 delta = e.wheelDelta/120;
3491             }else if(e.detail){ /* Mozilla case. */
3492                 delta = -e.detail/3;
3493             }
3494             return delta;
3495         },
3496
3497         /**
3498         * 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.
3499         * Example usage:<pre><code>
3500         // Handle click on any child of an element
3501         Ext.getBody().on('click', function(e){
3502             if(e.within('some-el')){
3503                 alert('Clicked on a child of some-el!');
3504             }
3505         });
3506
3507         // Handle click directly on an element, ignoring clicks on child nodes
3508         Ext.getBody().on('click', function(e,t){
3509             if((t.id == 'some-el') && !e.within(t, true)){
3510                 alert('Clicked directly on some-el!');
3511             }
3512         });
3513         </code></pre>
3514          * @param {Mixed} el The id, DOM element or Ext.Element to check
3515          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3516          * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3517          * @return {Boolean}
3518          */
3519         within : function(el, related, allowEl){
3520             if(el){
3521                 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3522                 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3523             }
3524             return false;
3525         }
3526      };
3527
3528     return new Ext.EventObjectImpl();
3529 }();
3530 /**
3531 * @class Ext.EventManager
3532 */
3533 Ext.apply(Ext.EventManager, function(){
3534    var resizeEvent,
3535        resizeTask,
3536        textEvent,
3537        textSize,
3538        D = Ext.lib.Dom,
3539        propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3540        curWidth = 0,
3541        curHeight = 0,
3542        // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3543        // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3544        // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3545        useKeydown = Ext.isWebKit ?
3546                    Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3547                    !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3548
3549    return {
3550        // private
3551        doResizeEvent: function(){
3552            var h = D.getViewHeight(),
3553                w = D.getViewWidth();
3554
3555             //whacky problem in IE where the resize event will fire even though the w/h are the same.
3556             if(curHeight != h || curWidth != w){
3557                resizeEvent.fire(curWidth = w, curHeight = h);
3558             }
3559        },
3560
3561        /**
3562         * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3563         * passes new viewport width and height to handlers.
3564         * @param {Function} fn      The handler function the window resize event invokes.
3565         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3566         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3567         */
3568        onWindowResize : function(fn, scope, options){
3569            if(!resizeEvent){
3570                resizeEvent = new Ext.util.Event();
3571                resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3572                Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3573            }
3574            resizeEvent.addListener(fn, scope, options);
3575        },
3576
3577        // exposed only to allow manual firing
3578        fireWindowResize : function(){
3579            if(resizeEvent){
3580                resizeTask.delay(100);
3581            }
3582        },
3583
3584        /**
3585         * 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.
3586         * @param {Function} fn      The function the event invokes.
3587         * @param {Object}   scope   The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3588         * @param {boolean}  options Options object as passed to {@link Ext.Element#addListener}
3589         */
3590        onTextResize : function(fn, scope, options){
3591            if(!textEvent){
3592                textEvent = new Ext.util.Event();
3593                var textEl = new Ext.Element(document.createElement('div'));
3594                textEl.dom.className = 'x-text-resize';
3595                textEl.dom.innerHTML = 'X';
3596                textEl.appendTo(document.body);
3597                textSize = textEl.dom.offsetHeight;
3598                setInterval(function(){
3599                    if(textEl.dom.offsetHeight != textSize){
3600                        textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3601                    }
3602                }, this.textResizeInterval);
3603            }
3604            textEvent.addListener(fn, scope, options);
3605        },
3606
3607        /**
3608         * Removes the passed window resize listener.
3609         * @param {Function} fn        The method the event invokes
3610         * @param {Object}   scope    The scope of handler
3611         */
3612        removeResizeListener : function(fn, scope){
3613            if(resizeEvent){
3614                resizeEvent.removeListener(fn, scope);
3615            }
3616        },
3617
3618        // private
3619        fireResize : function(){
3620            if(resizeEvent){
3621                resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3622            }
3623        },
3624
3625         /**
3626         * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3627         */
3628        textResizeInterval : 50,
3629
3630        /**
3631         * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3632         */
3633        ieDeferSrc : false,
3634
3635        // protected for use inside the framework
3636        // detects whether we should use keydown or keypress based on the browser.
3637        useKeydown: useKeydown
3638    };
3639 }());
3640
3641 Ext.EventManager.on = Ext.EventManager.addListener;
3642
3643
3644 Ext.apply(Ext.EventObjectImpl.prototype, {
3645    /** Key constant @type Number */
3646    BACKSPACE: 8,
3647    /** Key constant @type Number */
3648    TAB: 9,
3649    /** Key constant @type Number */
3650    NUM_CENTER: 12,
3651    /** Key constant @type Number */
3652    ENTER: 13,
3653    /** Key constant @type Number */
3654    RETURN: 13,
3655    /** Key constant @type Number */
3656    SHIFT: 16,
3657    /** Key constant @type Number */
3658    CTRL: 17,
3659    CONTROL : 17, // legacy
3660    /** Key constant @type Number */
3661    ALT: 18,
3662    /** Key constant @type Number */
3663    PAUSE: 19,
3664    /** Key constant @type Number */
3665    CAPS_LOCK: 20,
3666    /** Key constant @type Number */
3667    ESC: 27,
3668    /** Key constant @type Number */
3669    SPACE: 32,
3670    /** Key constant @type Number */
3671    PAGE_UP: 33,
3672    PAGEUP : 33, // legacy
3673    /** Key constant @type Number */
3674    PAGE_DOWN: 34,
3675    PAGEDOWN : 34, // legacy
3676    /** Key constant @type Number */
3677    END: 35,
3678    /** Key constant @type Number */
3679    HOME: 36,
3680    /** Key constant @type Number */
3681    LEFT: 37,
3682    /** Key constant @type Number */
3683    UP: 38,
3684    /** Key constant @type Number */
3685    RIGHT: 39,
3686    /** Key constant @type Number */
3687    DOWN: 40,
3688    /** Key constant @type Number */
3689    PRINT_SCREEN: 44,
3690    /** Key constant @type Number */
3691    INSERT: 45,
3692    /** Key constant @type Number */
3693    DELETE: 46,
3694    /** Key constant @type Number */
3695    ZERO: 48,
3696    /** Key constant @type Number */
3697    ONE: 49,
3698    /** Key constant @type Number */
3699    TWO: 50,
3700    /** Key constant @type Number */
3701    THREE: 51,
3702    /** Key constant @type Number */
3703    FOUR: 52,
3704    /** Key constant @type Number */
3705    FIVE: 53,
3706    /** Key constant @type Number */
3707    SIX: 54,
3708    /** Key constant @type Number */
3709    SEVEN: 55,
3710    /** Key constant @type Number */
3711    EIGHT: 56,
3712    /** Key constant @type Number */
3713    NINE: 57,
3714    /** Key constant @type Number */
3715    A: 65,
3716    /** Key constant @type Number */
3717    B: 66,
3718    /** Key constant @type Number */
3719    C: 67,
3720    /** Key constant @type Number */
3721    D: 68,
3722    /** Key constant @type Number */
3723    E: 69,
3724    /** Key constant @type Number */
3725    F: 70,
3726    /** Key constant @type Number */
3727    G: 71,
3728    /** Key constant @type Number */
3729    H: 72,
3730    /** Key constant @type Number */
3731    I: 73,
3732    /** Key constant @type Number */
3733    J: 74,
3734    /** Key constant @type Number */
3735    K: 75,
3736    /** Key constant @type Number */
3737    L: 76,
3738    /** Key constant @type Number */
3739    M: 77,
3740    /** Key constant @type Number */
3741    N: 78,
3742    /** Key constant @type Number */
3743    O: 79,
3744    /** Key constant @type Number */
3745    P: 80,
3746    /** Key constant @type Number */
3747    Q: 81,
3748    /** Key constant @type Number */
3749    R: 82,
3750    /** Key constant @type Number */
3751    S: 83,
3752    /** Key constant @type Number */
3753    T: 84,
3754    /** Key constant @type Number */
3755    U: 85,
3756    /** Key constant @type Number */
3757    V: 86,
3758    /** Key constant @type Number */
3759    W: 87,
3760    /** Key constant @type Number */
3761    X: 88,
3762    /** Key constant @type Number */
3763    Y: 89,
3764    /** Key constant @type Number */
3765    Z: 90,
3766    /** Key constant @type Number */
3767    CONTEXT_MENU: 93,
3768    /** Key constant @type Number */
3769    NUM_ZERO: 96,
3770    /** Key constant @type Number */
3771    NUM_ONE: 97,
3772    /** Key constant @type Number */
3773    NUM_TWO: 98,
3774    /** Key constant @type Number */
3775    NUM_THREE: 99,
3776    /** Key constant @type Number */
3777    NUM_FOUR: 100,
3778    /** Key constant @type Number */
3779    NUM_FIVE: 101,
3780    /** Key constant @type Number */
3781    NUM_SIX: 102,
3782    /** Key constant @type Number */
3783    NUM_SEVEN: 103,
3784    /** Key constant @type Number */
3785    NUM_EIGHT: 104,
3786    /** Key constant @type Number */
3787    NUM_NINE: 105,
3788    /** Key constant @type Number */
3789    NUM_MULTIPLY: 106,
3790    /** Key constant @type Number */
3791    NUM_PLUS: 107,
3792    /** Key constant @type Number */
3793    NUM_MINUS: 109,
3794    /** Key constant @type Number */
3795    NUM_PERIOD: 110,
3796    /** Key constant @type Number */
3797    NUM_DIVISION: 111,
3798    /** Key constant @type Number */
3799    F1: 112,
3800    /** Key constant @type Number */
3801    F2: 113,
3802    /** Key constant @type Number */
3803    F3: 114,
3804    /** Key constant @type Number */
3805    F4: 115,
3806    /** Key constant @type Number */
3807    F5: 116,
3808    /** Key constant @type Number */
3809    F6: 117,
3810    /** Key constant @type Number */
3811    F7: 118,
3812    /** Key constant @type Number */
3813    F8: 119,
3814    /** Key constant @type Number */
3815    F9: 120,
3816    /** Key constant @type Number */
3817    F10: 121,
3818    /** Key constant @type Number */
3819    F11: 122,
3820    /** Key constant @type Number */
3821    F12: 123,
3822
3823    /** @private */
3824    isNavKeyPress : function(){
3825        var me = this,
3826            k = this.normalizeKey(me.keyCode);
3827        return (k >= 33 && k <= 40) ||  // Page Up/Down, End, Home, Left, Up, Right, Down
3828        k == me.RETURN ||
3829        k == me.TAB ||
3830        k == me.ESC;
3831    },
3832
3833    isSpecialKey : function(){
3834        var k = this.normalizeKey(this.keyCode);
3835        return (this.type == 'keypress' && this.ctrlKey) ||
3836        this.isNavKeyPress() ||
3837        (k == this.BACKSPACE) || // Backspace
3838        (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3839        (k >= 44 && k <= 46);   // Print Screen, Insert, Delete
3840    },
3841
3842    getPoint : function(){
3843        return new Ext.lib.Point(this.xy[0], this.xy[1]);
3844    },
3845
3846    /**
3847     * Returns true if the control, meta, shift or alt key was pressed during this event.
3848     * @return {Boolean}
3849     */
3850    hasModifier : function(){
3851        return ((this.ctrlKey || this.altKey) || this.shiftKey);
3852    }
3853 });/**
3854  * @class Ext.Element
3855  * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3856  * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3857  * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3858  * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3859  * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3860  * Usage:<br>
3861 <pre><code>
3862 // by id
3863 var el = Ext.get("my-div");
3864
3865 // by DOM element reference
3866 var el = Ext.get(myDivElement);
3867 </code></pre>
3868  * <b>Animations</b><br />
3869  * <p>When an element is manipulated, by default there is no animation.</p>
3870  * <pre><code>
3871 var el = Ext.get("my-div");
3872
3873 // no animation
3874 el.setWidth(100);
3875  * </code></pre>
3876  * <p>Many of the functions for manipulating an element have an optional "animate" parameter.  This
3877  * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3878  * <pre><code>
3879 // default animation
3880 el.setWidth(100, true);
3881  * </code></pre>
3882  *
3883  * <p>To configure the effects, an object literal with animation options to use as the Element animation
3884  * configuration object can also be specified. Note that the supported Element animation configuration
3885  * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects.  The supported
3886  * Element animation configuration options are:</p>
3887 <pre>
3888 Option    Default   Description
3889 --------- --------  ---------------------------------------------
3890 {@link Ext.Fx#duration duration}  .35       The duration of the animation in seconds
3891 {@link Ext.Fx#easing easing}    easeOut   The easing method
3892 {@link Ext.Fx#callback callback}  none      A function to execute when the anim completes
3893 {@link Ext.Fx#scope scope}     this      The scope (this) of the callback function
3894 </pre>
3895  *
3896  * <pre><code>
3897 // Element animation options object
3898 var opt = {
3899     {@link Ext.Fx#duration duration}: 1,
3900     {@link Ext.Fx#easing easing}: 'elasticIn',
3901     {@link Ext.Fx#callback callback}: this.foo,
3902     {@link Ext.Fx#scope scope}: this
3903 };
3904 // animation with some options set
3905 el.setWidth(100, opt);
3906  * </code></pre>
3907  * <p>The Element animation object being used for the animation will be set on the options
3908  * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3909  * <pre><code>
3910 // using the "anim" property to get the Anim object
3911 if(opt.anim.isAnimated()){
3912     opt.anim.stop();
3913 }
3914  * </code></pre>
3915  * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3916  * <p><b> Composite (Collections of) Elements</b></p>
3917  * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3918  * @constructor Create a new Element directly.
3919  * @param {String/HTMLElement} element
3920  * @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).
3921  */
3922 (function(){
3923 var DOC = document;
3924
3925 Ext.Element = function(element, forceNew){
3926     var dom = typeof element == "string" ?
3927               DOC.getElementById(element) : element,
3928         id;
3929
3930     if(!dom) return null;
3931
3932     id = dom.id;
3933
3934     if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3935         return Ext.elCache[id].el;
3936     }
3937
3938     /**
3939      * The DOM element
3940      * @type HTMLElement
3941      */
3942     this.dom = dom;
3943
3944     /**
3945      * The DOM element ID
3946      * @type String
3947      */
3948     this.id = id || Ext.id(dom);
3949 };
3950
3951 var D = Ext.lib.Dom,
3952     DH = Ext.DomHelper,
3953     E = Ext.lib.Event,
3954     A = Ext.lib.Anim,
3955     El = Ext.Element,
3956     EC = Ext.elCache;
3957
3958 El.prototype = {
3959     /**
3960      * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3961      * @param {Object} o The object with the attributes
3962      * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3963      * @return {Ext.Element} this
3964      */
3965     set : function(o, useSet){
3966         var el = this.dom,
3967             attr,
3968             val,
3969             useSet = (useSet !== false) && !!el.setAttribute;
3970
3971         for(attr in o){
3972             if (o.hasOwnProperty(attr)) {
3973                 val = o[attr];
3974                 if (attr == 'style') {
3975                     DH.applyStyles(el, val);
3976                 } else if (attr == 'cls') {
3977                     el.className = val;
3978                 } else if (useSet) {
3979                     el.setAttribute(attr, val);
3980                 } else {
3981                     el[attr] = val;
3982                 }
3983             }
3984         }
3985         return this;
3986     },
3987
3988 //  Mouse events
3989     /**
3990      * @event click
3991      * Fires when a mouse click is detected within the element.
3992      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3993      * @param {HtmlElement} t The target of the event.
3994      * @param {Object} o The options configuration passed to the {@link #addListener} call.
3995      */
3996     /**
3997      * @event contextmenu
3998      * Fires when a right click is detected within the element.
3999      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4000      * @param {HtmlElement} t The target of the event.
4001      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4002      */
4003     /**
4004      * @event dblclick
4005      * Fires when a mouse double click is detected within the element.
4006      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4007      * @param {HtmlElement} t The target of the event.
4008      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4009      */
4010     /**
4011      * @event mousedown
4012      * Fires when a mousedown is detected within the element.
4013      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4014      * @param {HtmlElement} t The target of the event.
4015      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4016      */
4017     /**
4018      * @event mouseup
4019      * Fires when a mouseup is detected within the element.
4020      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4021      * @param {HtmlElement} t The target of the event.
4022      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4023      */
4024     /**
4025      * @event mouseover
4026      * Fires when a mouseover is detected within the element.
4027      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4028      * @param {HtmlElement} t The target of the event.
4029      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4030      */
4031     /**
4032      * @event mousemove
4033      * Fires when a mousemove is detected with the element.
4034      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4035      * @param {HtmlElement} t The target of the event.
4036      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4037      */
4038     /**
4039      * @event mouseout
4040      * Fires when a mouseout is detected with the element.
4041      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4042      * @param {HtmlElement} t The target of the event.
4043      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4044      */
4045     /**
4046      * @event mouseenter
4047      * Fires when the mouse enters the element.
4048      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4049      * @param {HtmlElement} t The target of the event.
4050      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4051      */
4052     /**
4053      * @event mouseleave
4054      * Fires when the mouse leaves the element.
4055      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4056      * @param {HtmlElement} t The target of the event.
4057      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4058      */
4059
4060 //  Keyboard events
4061     /**
4062      * @event keypress
4063      * Fires when a keypress is detected within the element.
4064      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4065      * @param {HtmlElement} t The target of the event.
4066      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4067      */
4068     /**
4069      * @event keydown
4070      * Fires when a keydown is detected within the element.
4071      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4072      * @param {HtmlElement} t The target of the event.
4073      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4074      */
4075     /**
4076      * @event keyup
4077      * Fires when a keyup is detected within the element.
4078      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4079      * @param {HtmlElement} t The target of the event.
4080      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4081      */
4082
4083
4084 //  HTML frame/object events
4085     /**
4086      * @event load
4087      * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
4088      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4089      * @param {HtmlElement} t The target of the event.
4090      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4091      */
4092     /**
4093      * @event unload
4094      * 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.
4095      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4096      * @param {HtmlElement} t The target of the event.
4097      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4098      */
4099     /**
4100      * @event abort
4101      * Fires when an object/image is stopped from loading before completely loaded.
4102      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4103      * @param {HtmlElement} t The target of the event.
4104      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4105      */
4106     /**
4107      * @event error
4108      * Fires when an object/image/frame cannot be loaded properly.
4109      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4110      * @param {HtmlElement} t The target of the event.
4111      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4112      */
4113     /**
4114      * @event resize
4115      * Fires when a document view is resized.
4116      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4117      * @param {HtmlElement} t The target of the event.
4118      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4119      */
4120     /**
4121      * @event scroll
4122      * Fires when a document view is scrolled.
4123      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4124      * @param {HtmlElement} t The target of the event.
4125      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4126      */
4127
4128 //  Form events
4129     /**
4130      * @event select
4131      * Fires when a user selects some text in a text field, including input and textarea.
4132      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4133      * @param {HtmlElement} t The target of the event.
4134      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4135      */
4136     /**
4137      * @event change
4138      * Fires when a control loses the input focus and its value has been modified since gaining focus.
4139      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4140      * @param {HtmlElement} t The target of the event.
4141      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4142      */
4143     /**
4144      * @event submit
4145      * Fires when a form is submitted.
4146      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4147      * @param {HtmlElement} t The target of the event.
4148      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4149      */
4150     /**
4151      * @event reset
4152      * Fires when a form is reset.
4153      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4154      * @param {HtmlElement} t The target of the event.
4155      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4156      */
4157     /**
4158      * @event focus
4159      * Fires when an element receives focus either via the pointing device or by tab navigation.
4160      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4161      * @param {HtmlElement} t The target of the event.
4162      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4163      */
4164     /**
4165      * @event blur
4166      * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4167      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4168      * @param {HtmlElement} t The target of the event.
4169      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4170      */
4171
4172 //  User Interface events
4173     /**
4174      * @event DOMFocusIn
4175      * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4176      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4177      * @param {HtmlElement} t The target of the event.
4178      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4179      */
4180     /**
4181      * @event DOMFocusOut
4182      * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4183      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4184      * @param {HtmlElement} t The target of the event.
4185      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4186      */
4187     /**
4188      * @event DOMActivate
4189      * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4190      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4191      * @param {HtmlElement} t The target of the event.
4192      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4193      */
4194
4195 //  DOM Mutation events
4196     /**
4197      * @event DOMSubtreeModified
4198      * Where supported. Fires when the subtree is modified.
4199      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4200      * @param {HtmlElement} t The target of the event.
4201      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4202      */
4203     /**
4204      * @event DOMNodeInserted
4205      * Where supported. Fires when a node has been added as a child of another node.
4206      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4207      * @param {HtmlElement} t The target of the event.
4208      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4209      */
4210     /**
4211      * @event DOMNodeRemoved
4212      * Where supported. Fires when a descendant node of the element is removed.
4213      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4214      * @param {HtmlElement} t The target of the event.
4215      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4216      */
4217     /**
4218      * @event DOMNodeRemovedFromDocument
4219      * Where supported. Fires when a node is being removed from a document.
4220      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4221      * @param {HtmlElement} t The target of the event.
4222      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4223      */
4224     /**
4225      * @event DOMNodeInsertedIntoDocument
4226      * Where supported. Fires when a node is being inserted into a document.
4227      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4228      * @param {HtmlElement} t The target of the event.
4229      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4230      */
4231     /**
4232      * @event DOMAttrModified
4233      * Where supported. Fires when an attribute has been modified.
4234      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4235      * @param {HtmlElement} t The target of the event.
4236      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4237      */
4238     /**
4239      * @event DOMCharacterDataModified
4240      * Where supported. Fires when the character data has been modified.
4241      * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4242      * @param {HtmlElement} t The target of the event.
4243      * @param {Object} o The options configuration passed to the {@link #addListener} call.
4244      */
4245
4246     /**
4247      * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4248      * @type String
4249      */
4250     defaultUnit : "px",
4251
4252     /**
4253      * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4254      * @param {String} selector The simple selector to test
4255      * @return {Boolean} True if this element matches the selector, else false
4256      */
4257     is : function(simpleSelector){
4258         return Ext.DomQuery.is(this.dom, simpleSelector);
4259     },
4260
4261     /**
4262      * Tries to focus the element. Any exceptions are caught and ignored.
4263      * @param {Number} defer (optional) Milliseconds to defer the focus
4264      * @return {Ext.Element} this
4265      */
4266     focus : function(defer, /* private */ dom) {
4267         var me = this,
4268             dom = dom || me.dom;
4269         try{
4270             if(Number(defer)){
4271                 me.focus.defer(defer, null, [null, dom]);
4272             }else{
4273                 dom.focus();
4274             }
4275         }catch(e){}
4276         return me;
4277     },
4278
4279     /**
4280      * Tries to blur the element. Any exceptions are caught and ignored.
4281      * @return {Ext.Element} this
4282      */
4283     blur : function() {
4284         try{
4285             this.dom.blur();
4286         }catch(e){}
4287         return this;
4288     },
4289
4290     /**
4291      * Returns the value of the "value" attribute
4292      * @param {Boolean} asNumber true to parse the value as a number
4293      * @return {String/Number}
4294      */
4295     getValue : function(asNumber){
4296         var val = this.dom.value;
4297         return asNumber ? parseInt(val, 10) : val;
4298     },
4299
4300     /**
4301      * Appends an event handler to this element.  The shorthand version {@link #on} is equivalent.
4302      * @param {String} eventName The name of event to handle.
4303      * @param {Function} fn The handler function the event invokes. This function is passed
4304      * the following parameters:<ul>
4305      * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4306      * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4307      * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4308      * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4309      * </ul>
4310      * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4311      * <b>If omitted, defaults to this Element.</b>.
4312      * @param {Object} options (optional) An object containing handler configuration properties.
4313      * This may contain any of the following properties:<ul>
4314      * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4315      * <b>If omitted, defaults to this Element.</b></div></li>
4316      * <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>
4317      * <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>
4318      * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4319      * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4320      * <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>
4321      * <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>
4322      * <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>
4323      * <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>
4324      * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4325      * by the specified number of milliseconds. If the event fires again within that time, the original
4326      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4327      * </ul><br>
4328      * <p>
4329      * <b>Combining Options</b><br>
4330      * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4331      * addListener.  The two are equivalent.  Using the options argument, it is possible to combine different
4332      * types of listeners:<br>
4333      * <br>
4334      * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4335      * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4336      * Code:<pre><code>
4337 el.on('click', this.onClick, this, {
4338     single: true,
4339     delay: 100,
4340     stopEvent : true,
4341     forumId: 4
4342 });</code></pre></p>
4343      * <p>
4344      * <b>Attaching multiple handlers in 1 call</b><br>
4345      * The method also allows for a single argument to be passed which is a config object containing properties
4346      * which specify multiple handlers.</p>
4347      * <p>
4348      * Code:<pre><code>
4349 el.on({
4350     'click' : {
4351         fn: this.onClick,
4352         scope: this,
4353         delay: 100
4354     },
4355     'mouseover' : {
4356         fn: this.onMouseOver,
4357         scope: this
4358     },
4359     'mouseout' : {
4360         fn: this.onMouseOut,
4361         scope: this
4362     }
4363 });</code></pre>
4364      * <p>
4365      * Or a shorthand syntax:<br>
4366      * Code:<pre><code></p>
4367 el.on({
4368     'click' : this.onClick,
4369     'mouseover' : this.onMouseOver,
4370     'mouseout' : this.onMouseOut,
4371     scope: this
4372 });
4373      * </code></pre></p>
4374      * <p><b>delegate</b></p>
4375      * <p>This is a configuration option that you can pass along when registering a handler for
4376      * an event to assist with event delegation. Event delegation is a technique that is used to
4377      * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4378      * for a container element as opposed to each element within a container. By setting this
4379      * configuration option to a simple selector, the target element will be filtered to look for
4380      * a descendant of the target.
4381      * For example:<pre><code>
4382 // using this markup:
4383 &lt;div id='elId'>
4384     &lt;p id='p1'>paragraph one&lt;/p>
4385     &lt;p id='p2' class='clickable'>paragraph two&lt;/p>
4386     &lt;p id='p3'>paragraph three&lt;/p>
4387 &lt;/div>
4388 // utilize event delegation to registering just one handler on the container element:
4389 el = Ext.get('elId');
4390 el.on(
4391     'click',
4392     function(e,t) {
4393         // handle click
4394         console.info(t.id); // 'p2'
4395     },
4396     this,
4397     {
4398         // filter the target element to be a descendant with the class 'clickable'
4399         delegate: '.clickable'
4400     }
4401 );
4402      * </code></pre></p>
4403      * @return {Ext.Element} this
4404      */
4405     addListener : function(eventName, fn, scope, options){
4406         Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
4407         return this;
4408     },
4409
4410     /**
4411      * Removes an event handler from this element.  The shorthand version {@link #un} is equivalent.
4412      * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4413      * listener, the same scope must be specified here.
4414      * Example:
4415      * <pre><code>
4416 el.removeListener('click', this.handlerFn);
4417 // or
4418 el.un('click', this.handlerFn);
4419 </code></pre>
4420      * @param {String} eventName The name of the event from which to remove the handler.
4421      * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4422      * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4423      * then this must refer to the same object.
4424      * @return {Ext.Element} this
4425      */
4426     removeListener : function(eventName, fn, scope){
4427         Ext.EventManager.removeListener(this.dom,  eventName, fn, scope || this);
4428         return this;
4429     },
4430
4431     /**
4432      * Removes all previous added listeners from this element
4433      * @return {Ext.Element} this
4434      */
4435     removeAllListeners : function(){
4436         Ext.EventManager.removeAll(this.dom);
4437         return this;
4438     },
4439
4440     /**
4441      * Recursively removes all previous added listeners from this element and its children
4442      * @return {Ext.Element} this
4443      */
4444     purgeAllListeners : function() {
4445         Ext.EventManager.purgeElement(this, true);
4446         return this;
4447     },
4448     /**
4449      * @private Test if size has a unit, otherwise appends the default
4450      */
4451     addUnits : function(size){
4452         if(size === "" || size == "auto" || size === undefined){
4453             size = size || '';
4454         } else if(!isNaN(size) || !unitPattern.test(size)){
4455             size = size + (this.defaultUnit || 'px');
4456         }
4457         return size;
4458     },
4459
4460     /**
4461      * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4462      * 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>
4463      * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt>&lt;script></tt> elements. This is a browser restriction.</p>
4464      * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4465      * exactly how to request the HTML.
4466      * @return {Ext.Element} this
4467      */
4468     load : function(url, params, cb){
4469         Ext.Ajax.request(Ext.apply({
4470             params: params,
4471             url: url.url || url,
4472             callback: cb,
4473             el: this.dom,
4474             indicatorText: url.indicatorText || ''
4475         }, Ext.isObject(url) ? url : {}));
4476         return this;
4477     },
4478
4479     /**
4480      * Tests various css rules/browsers to determine if this element uses a border box
4481      * @return {Boolean}
4482      */
4483     isBorderBox : function(){
4484         return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4485     },
4486
4487     /**
4488      * <p>Removes this element's dom reference.  Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4489      */
4490     remove : function(){
4491         var me = this,
4492             dom = me.dom;
4493
4494         if (dom) {
4495             delete me.dom;
4496             Ext.removeNode(dom);
4497         }
4498     },
4499
4500     /**
4501      * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4502      * @param {Function} overFn The function to call when the mouse enters the Element.
4503      * @param {Function} outFn The function to call when the mouse leaves the Element.
4504      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4505      * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4506      * @return {Ext.Element} this
4507      */
4508     hover : function(overFn, outFn, scope, options){
4509         var me = this;
4510         me.on('mouseenter', overFn, scope || me.dom, options);
4511         me.on('mouseleave', outFn, scope || me.dom, options);
4512         return me;
4513     },
4514
4515     /**
4516      * Returns true if this element is an ancestor of the passed element
4517      * @param {HTMLElement/String} el The element to check
4518      * @return {Boolean} True if this element is an ancestor of el, else false
4519      */
4520     contains : function(el){
4521         return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4522     },
4523
4524     /**
4525      * Returns the value of a namespaced attribute from the element's underlying DOM node.
4526      * @param {String} namespace The namespace in which to look for the attribute
4527      * @param {String} name The attribute name
4528      * @return {String} The attribute value
4529      * @deprecated
4530      */
4531     getAttributeNS : function(ns, name){
4532         return this.getAttribute(name, ns);
4533     },
4534
4535     /**
4536      * Returns the value of an attribute from the element's underlying DOM node.
4537      * @param {String} name The attribute name
4538      * @param {String} namespace (optional) The namespace in which to look for the attribute
4539      * @return {String} The attribute value
4540      */
4541     getAttribute : Ext.isIE ? function(name, ns){
4542         var d = this.dom,
4543             type = typeof d[ns + ":" + name];
4544
4545         if(['undefined', 'unknown'].indexOf(type) == -1){
4546             return d[ns + ":" + name];
4547         }
4548         return d[name];
4549     } : function(name, ns){
4550         var d = this.dom;
4551         return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4552     },
4553
4554     /**
4555     * Update the innerHTML of this element
4556     * @param {String} html The new HTML
4557     * @return {Ext.Element} this
4558      */
4559     update : function(html) {
4560         if (this.dom) {
4561             this.dom.innerHTML = html;
4562         }
4563         return this;
4564     }
4565 };
4566
4567 var ep = El.prototype;
4568
4569 El.addMethods = function(o){
4570    Ext.apply(ep, o);
4571 };
4572
4573 /**
4574  * Appends an event handler (shorthand for {@link #addListener}).
4575  * @param {String} eventName The name of event to handle.
4576  * @param {Function} fn The handler function the event invokes.
4577  * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4578  * @param {Object} options (optional) An object containing standard {@link #addListener} options
4579  * @member Ext.Element
4580  * @method on
4581  */
4582 ep.on = ep.addListener;
4583
4584 /**
4585  * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4586  * @param {String} eventName The name of the event from which to remove the handler.
4587  * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4588  * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4589  * then this must refer to the same object.
4590  * @return {Ext.Element} this
4591  * @member Ext.Element
4592  * @method un
4593  */
4594 ep.un = ep.removeListener;
4595
4596 /**
4597  * true to automatically adjust width and height settings for box-model issues (default to true)
4598  */
4599 ep.autoBoxAdjust = true;
4600
4601 // private
4602 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4603     docEl;
4604
4605 /**
4606  * @private
4607  */
4608
4609 /**
4610  * Retrieves Ext.Element objects.
4611  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4612  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4613  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4614  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4615  * object was recreated with the same id via AJAX or DOM.</p>
4616  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4617  * @return {Element} The Element object (or null if no matching element was found)
4618  * @static
4619  * @member Ext.Element
4620  * @method get
4621  */
4622 El.get = function(el){
4623     var ex,
4624         elm,
4625         id;
4626     if(!el){ return null; }
4627     if (typeof el == "string") { // element id
4628         if (!(elm = DOC.getElementById(el))) {
4629             return null;
4630         }
4631         if (EC[el] && EC[el].el) {
4632             ex = EC[el].el;
4633             ex.dom = elm;
4634         } else {
4635             ex = El.addToCache(new El(elm));
4636         }
4637         return ex;
4638     } else if (el.tagName) { // dom element
4639         if(!(id = el.id)){
4640             id = Ext.id(el);
4641         }
4642         if (EC[id] && EC[id].el) {
4643             ex = EC[id].el;
4644             ex.dom = el;
4645         } else {
4646             ex = El.addToCache(new El(el));
4647         }
4648         return ex;
4649     } else if (el instanceof El) {
4650         if(el != docEl){
4651             // refresh dom element in case no longer valid,
4652             // catch case where it hasn't been appended
4653
4654             // If an el instance is passed, don't pass to getElementById without some kind of id
4655             if (Ext.isIE && (el.id == undefined || el.id == '')) {
4656                 el.dom = el.dom;
4657             } else {
4658                 el.dom = DOC.getElementById(el.id) || el.dom;
4659             }
4660         }
4661         return el;
4662     } else if(el.isComposite) {
4663         return el;
4664     } else if(Ext.isArray(el)) {
4665         return El.select(el);
4666     } else if(el == DOC) {
4667         // create a bogus element object representing the document object
4668         if(!docEl){
4669             var f = function(){};
4670             f.prototype = El.prototype;
4671             docEl = new f();
4672             docEl.dom = DOC;
4673         }
4674         return docEl;
4675     }
4676     return null;
4677 };
4678
4679 El.addToCache = function(el, id){
4680     id = id || el.id;
4681     EC[id] = {
4682         el:  el,
4683         data: {},
4684         events: {}
4685     };
4686     return el;
4687 };
4688
4689 // private method for getting and setting element data
4690 El.data = function(el, key, value){
4691     el = El.get(el);
4692     if (!el) {
4693         return null;
4694     }
4695     var c = EC[el.id].data;
4696     if(arguments.length == 2){
4697         return c[key];
4698     }else{
4699         return (c[key] = value);
4700     }
4701 };
4702
4703 // private
4704 // Garbage collection - uncache elements/purge listeners on orphaned elements
4705 // so we don't hold a reference and cause the browser to retain them
4706 function garbageCollect(){
4707     if(!Ext.enableGarbageCollector){
4708         clearInterval(El.collectorThreadId);
4709     } else {
4710         var eid,
4711             el,
4712             d,
4713             o;
4714
4715         for(eid in EC){
4716             o = EC[eid];
4717             if(o.skipGC){
4718                 continue;
4719             }
4720             el = o.el;
4721             d = el.dom;
4722             // -------------------------------------------------------
4723             // Determining what is garbage:
4724             // -------------------------------------------------------
4725             // !d
4726             // dom node is null, definitely garbage
4727             // -------------------------------------------------------
4728             // !d.parentNode
4729             // no parentNode == direct orphan, definitely garbage
4730             // -------------------------------------------------------
4731             // !d.offsetParent && !document.getElementById(eid)
4732             // display none elements have no offsetParent so we will
4733             // also try to look it up by it's id. However, check
4734             // offsetParent first so we don't do unneeded lookups.
4735             // This enables collection of elements that are not orphans
4736             // directly, but somewhere up the line they have an orphan
4737             // parent.
4738             // -------------------------------------------------------
4739             if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4740                 if(Ext.enableListenerCollection){
4741                     Ext.EventManager.removeAll(d);
4742                 }
4743                 delete EC[eid];
4744             }
4745         }
4746         // Cleanup IE Object leaks
4747         if (Ext.isIE) {
4748             var t = {};
4749             for (eid in EC) {
4750                 t[eid] = EC[eid];
4751             }
4752             EC = Ext.elCache = t;
4753         }
4754     }
4755 }
4756 El.collectorThreadId = setInterval(garbageCollect, 30000);
4757
4758 var flyFn = function(){};
4759 flyFn.prototype = El.prototype;
4760
4761 // dom is optional
4762 El.Flyweight = function(dom){
4763     this.dom = dom;
4764 };
4765
4766 El.Flyweight.prototype = new flyFn();
4767 El.Flyweight.prototype.isFlyweight = true;
4768 El._flyweights = {};
4769
4770 /**
4771  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4772  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4773  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4774  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4775  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4776  * @param {String/HTMLElement} el The dom node or id
4777  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4778  * (e.g. internally Ext uses "_global")
4779  * @return {Element} The shared Element object (or null if no matching element was found)
4780  * @member Ext.Element
4781  * @method fly
4782  */
4783 El.fly = function(el, named){
4784     var ret = null;
4785     named = named || '_global';
4786
4787     if (el = Ext.getDom(el)) {
4788         (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4789         ret = El._flyweights[named];
4790     }
4791     return ret;
4792 };
4793
4794 /**
4795  * Retrieves Ext.Element objects.
4796  * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4797  * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4798  * its ID, use {@link Ext.ComponentMgr#get}.</p>
4799  * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4800  * object was recreated with the same id via AJAX or DOM.</p>
4801  * Shorthand of {@link Ext.Element#get}
4802  * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4803  * @return {Element} The Element object (or null if no matching element was found)
4804  * @member Ext
4805  * @method get
4806  */
4807 Ext.get = El.get;
4808
4809 /**
4810  * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4811  * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4812  * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4813  * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4814  * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4815  * @param {String/HTMLElement} el The dom node or id
4816  * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4817  * (e.g. internally Ext uses "_global")
4818  * @return {Element} The shared Element object (or null if no matching element was found)
4819  * @member Ext
4820  * @method fly
4821  */
4822 Ext.fly = El.fly;
4823
4824 // speedy lookup for elements never to box adjust
4825 var noBoxAdjust = Ext.isStrict ? {
4826     select:1
4827 } : {
4828     input:1, select:1, textarea:1
4829 };
4830 if(Ext.isIE || Ext.isGecko){
4831     noBoxAdjust['button'] = 1;
4832 }
4833
4834 })();
4835 /**
4836  * @class Ext.Element
4837  */
4838 Ext.Element.addMethods({
4839     /**
4840      * Stops the specified event(s) from bubbling and optionally prevents the default action
4841      * @param {String/Array} eventName an event / array of events to stop from bubbling
4842      * @param {Boolean} preventDefault (optional) true to prevent the default action too
4843      * @return {Ext.Element} this
4844      */
4845     swallowEvent : function(eventName, preventDefault){
4846         var me = this;
4847         function fn(e){
4848             e.stopPropagation();
4849             if(preventDefault){
4850                 e.preventDefault();
4851             }
4852         }
4853         if(Ext.isArray(eventName)){
4854             Ext.each(eventName, function(e) {
4855                  me.on(e, fn);
4856             });
4857             return me;
4858         }
4859         me.on(eventName, fn);
4860         return me;
4861     },
4862
4863     /**
4864      * Create an event handler on this element such that when the event fires and is handled by this element,
4865      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4866      * @param {String} eventName The type of event to relay
4867      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4868      * for firing the relayed event
4869      */
4870     relayEvent : function(eventName, observable){
4871         this.on(eventName, function(e){
4872             observable.fireEvent(eventName, e);
4873         });
4874     },
4875
4876     /**
4877      * Removes worthless text nodes
4878      * @param {Boolean} forceReclean (optional) By default the element
4879      * keeps track if it has been cleaned already so
4880      * you can call this over and over. However, if you update the element and
4881      * need to force a reclean, you can pass true.
4882      */
4883     clean : function(forceReclean){
4884         var me = this,
4885             dom = me.dom,
4886             n = dom.firstChild,
4887             ni = -1;
4888
4889         if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4890             return me;
4891         }
4892
4893         while(n){
4894             var nx = n.nextSibling;
4895             if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4896                 dom.removeChild(n);
4897             }else{
4898                 n.nodeIndex = ++ni;
4899             }
4900             n = nx;
4901         }
4902         Ext.Element.data(dom, 'isCleaned', true);
4903         return me;
4904     },
4905
4906     /**
4907      * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4908      * parameter as {@link Ext.Updater#update}
4909      * @return {Ext.Element} this
4910      */
4911     load : function(){
4912         var um = this.getUpdater();
4913         um.update.apply(um, arguments);
4914         return this;
4915     },
4916
4917     /**
4918     * Gets this element's {@link Ext.Updater Updater}
4919     * @return {Ext.Updater} The Updater
4920     */
4921     getUpdater : function(){
4922         return this.updateManager || (this.updateManager = new Ext.Updater(this));
4923     },
4924
4925     /**
4926     * Update the innerHTML of this element, optionally searching for and processing scripts
4927     * @param {String} html The new HTML
4928     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4929     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4930     * @return {Ext.Element} this
4931      */
4932     update : function(html, loadScripts, callback){
4933         if (!this.dom) {
4934             return this;
4935         }
4936         html = html || "";
4937
4938         if(loadScripts !== true){
4939             this.dom.innerHTML = html;
4940             if(typeof callback == 'function'){
4941                 callback();
4942             }
4943             return this;
4944         }
4945
4946         var id = Ext.id(),
4947             dom = this.dom;
4948
4949         html += '<span id="' + id + '"></span>';
4950
4951         Ext.lib.Event.onAvailable(id, function(){
4952             var DOC = document,
4953                 hd = DOC.getElementsByTagName("head")[0],
4954                 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4955                 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4956                 typeRe = /\stype=([\'\"])(.*?)\1/i,
4957                 match,
4958                 attrs,
4959                 srcMatch,
4960                 typeMatch,
4961                 el,
4962                 s;
4963
4964             while((match = re.exec(html))){
4965                 attrs = match[1];
4966                 srcMatch = attrs ? attrs.match(srcRe) : false;
4967                 if(srcMatch && srcMatch[2]){
4968                    s = DOC.createElement("script");
4969                    s.src = srcMatch[2];
4970                    typeMatch = attrs.match(typeRe);
4971                    if(typeMatch && typeMatch[2]){
4972                        s.type = typeMatch[2];
4973                    }
4974                    hd.appendChild(s);
4975                 }else if(match[2] && match[2].length > 0){
4976                     if(window.execScript) {
4977                        window.execScript(match[2]);
4978                     } else {
4979                        window.eval(match[2]);
4980                     }
4981                 }
4982             }
4983             el = DOC.getElementById(id);
4984             if(el){Ext.removeNode(el);}
4985             if(typeof callback == 'function'){
4986                 callback();
4987             }
4988         });
4989         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4990         return this;
4991     },
4992
4993     // inherit docs, overridden so we can add removeAnchor
4994     removeAllListeners : function(){
4995         this.removeAnchor();
4996         Ext.EventManager.removeAll(this.dom);
4997         return this;
4998     },
4999
5000     /**
5001      * Creates a proxy element of this element
5002      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
5003      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
5004      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
5005      * @return {Ext.Element} The new proxy element
5006      */
5007     createProxy : function(config, renderTo, matchBox){
5008         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
5009
5010         var me = this,
5011             proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
5012                                Ext.DomHelper.insertBefore(me.dom, config, true);
5013
5014         if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
5015            proxy.setBox(me.getBox());
5016         }
5017         return proxy;
5018     }
5019 });
5020
5021 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
5022 /**
5023  * @class Ext.Element
5024  */
5025 Ext.Element.addMethods({
5026     /**
5027      * Gets the x,y coordinates specified by the anchor position on the element.
5028      * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo}
5029      * for details on supported anchor positions.
5030      * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5031      * of page coordinates
5032      * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5033      * {width: (target width), height: (target height)} (defaults to the element's current size)
5034      * @return {Array} [x, y] An array containing the element's x and y coordinates
5035      */
5036     getAnchorXY : function(anchor, local, s){
5037         //Passing a different size is useful for pre-calculating anchors,
5038         //especially for anchored animations that change the el size.
5039                 anchor = (anchor || "tl").toLowerCase();
5040         s = s || {};
5041         
5042         var me = this,        
5043                 vp = me.dom == document.body || me.dom == document,
5044                 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5045                 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),                              
5046                 xy,             
5047                 r = Math.round,
5048                 o = me.getXY(),
5049                 scroll = me.getScroll(),
5050                 extraX = vp ? scroll.left : !local ? o[0] : 0,
5051                 extraY = vp ? scroll.top : !local ? o[1] : 0,
5052                 hash = {
5053                         c  : [r(w * 0.5), r(h * 0.5)],
5054                         t  : [r(w * 0.5), 0],
5055                         l  : [0, r(h * 0.5)],
5056                         r  : [w, r(h * 0.5)],
5057                         b  : [r(w * 0.5), h],
5058                         tl : [0, 0],    
5059                         bl : [0, h],
5060                         br : [w, h],
5061                         tr : [w, 0]
5062                 };
5063         
5064         xy = hash[anchor];      
5065         return [xy[0] + extraX, xy[1] + extraY]; 
5066     },
5067
5068     /**
5069      * Anchors an element to another element and realigns it when the window is resized.
5070      * @param {Mixed} element The element to align to.
5071      * @param {String} position The position to align to.
5072      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5073      * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5074      * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5075      * is a number, it is used as the buffer delay (defaults to 50ms).
5076      * @param {Function} callback The function to call after the animation finishes
5077      * @return {Ext.Element} this
5078      */
5079     anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){        
5080             var me = this,
5081             dom = me.dom,
5082             scroll = !Ext.isEmpty(monitorScroll),
5083             action = function(){
5084                 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5085                 Ext.callback(callback, Ext.fly(dom));
5086             },
5087             anchor = this.getAnchor();
5088             
5089         // previous listener anchor, remove it
5090         this.removeAnchor();
5091         Ext.apply(anchor, {
5092             fn: action,
5093             scroll: scroll
5094         });
5095
5096         Ext.EventManager.onWindowResize(action, null);
5097         
5098         if(scroll){
5099             Ext.EventManager.on(window, 'scroll', action, null,
5100                 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5101         }
5102         action.call(me); // align immediately
5103         return me;
5104     },
5105     
5106     /**
5107      * Remove any anchor to this element. See {@link #anchorTo}.
5108      * @return {Ext.Element} this
5109      */
5110     removeAnchor : function(){
5111         var me = this,
5112             anchor = this.getAnchor();
5113             
5114         if(anchor && anchor.fn){
5115             Ext.EventManager.removeResizeListener(anchor.fn);
5116             if(anchor.scroll){
5117                 Ext.EventManager.un(window, 'scroll', anchor.fn);
5118             }
5119             delete anchor.fn;
5120         }
5121         return me;
5122     },
5123     
5124     // private
5125     getAnchor : function(){
5126         var data = Ext.Element.data,
5127             dom = this.dom;
5128             if (!dom) {
5129                 return;
5130             }
5131             var anchor = data(dom, '_anchor');
5132             
5133         if(!anchor){
5134             anchor = data(dom, '_anchor', {});
5135         }
5136         return anchor;
5137     },
5138
5139     /**
5140      * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5141      * supported position values.
5142      * @param {Mixed} element The element to align to.
5143      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5144      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5145      * @return {Array} [x, y]
5146      */
5147     getAlignToXY : function(el, p, o){      
5148         el = Ext.get(el);
5149         
5150         if(!el || !el.dom){
5151             throw "Element.alignToXY with an element that doesn't exist";
5152         }
5153         
5154         o = o || [0,0];
5155         p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();       
5156                 
5157         var me = this,
5158                 d = me.dom,
5159                 a1,
5160                 a2,
5161                 x,
5162                 y,
5163                 //constrain the aligned el to viewport if necessary
5164                 w,
5165                 h,
5166                 r,
5167                 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5168                 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5169                 p1y,
5170                 p1x,            
5171                 p2y,
5172                 p2x,
5173                 swapY,
5174                 swapX,
5175                 doc = document,
5176                 docElement = doc.documentElement,
5177                 docBody = doc.body,
5178                 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5179                 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5180                 c = false, //constrain to viewport
5181                 p1 = "", 
5182                 p2 = "",
5183                 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5184         
5185         if(!m){
5186            throw "Element.alignTo with an invalid alignment " + p;
5187         }
5188         
5189         p1 = m[1]; 
5190         p2 = m[2]; 
5191         c = !!m[3];
5192
5193         //Subtract the aligned el's internal xy from the target's offset xy
5194         //plus custom offset to get the aligned el's new offset xy
5195         a1 = me.getAnchorXY(p1, true);
5196         a2 = el.getAnchorXY(p2, false);
5197
5198         x = a2[0] - a1[0] + o[0];
5199         y = a2[1] - a1[1] + o[1];
5200
5201         if(c){    
5202                w = me.getWidth();
5203            h = me.getHeight();
5204            r = el.getRegion();       
5205            //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5206            //perpendicular to the vp border, allow the aligned el to slide on that border,
5207            //otherwise swap the aligned el to the opposite border of the target.
5208            p1y = p1.charAt(0);
5209            p1x = p1.charAt(p1.length-1);
5210            p2y = p2.charAt(0);
5211            p2x = p2.charAt(p2.length-1);
5212            swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5213            swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));          
5214            
5215
5216            if (x + w > dw + scrollX) {
5217                 x = swapX ? r.left-w : dw+scrollX-w;
5218            }
5219            if (x < scrollX) {
5220                x = swapX ? r.right : scrollX;
5221            }
5222            if (y + h > dh + scrollY) {
5223                 y = swapY ? r.top-h : dh+scrollY-h;
5224             }
5225            if (y < scrollY){
5226                y = swapY ? r.bottom : scrollY;
5227            }
5228         }
5229         return [x,y];
5230     },
5231
5232     /**
5233      * Aligns this element with another element relative to the specified anchor points. If the other element is the
5234      * document it aligns it to the viewport.
5235      * The position parameter is optional, and can be specified in any one of the following formats:
5236      * <ul>
5237      *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5238      *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5239      *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
5240      *       deprecated in favor of the newer two anchor syntax below</i>.</li>
5241      *   <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
5242      *       element's anchor point, and the second value is used as the target's anchor point.</li>
5243      * </ul>
5244      * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
5245      * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5246      * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
5247      * that specified in order to enforce the viewport constraints.
5248      * Following are all of the supported anchor positions:
5249 <pre>
5250 Value  Description
5251 -----  -----------------------------
5252 tl     The top left corner (default)
5253 t      The center of the top edge
5254 tr     The top right corner
5255 l      The center of the left edge
5256 c      In the center of the element
5257 r      The center of the right edge
5258 bl     The bottom left corner
5259 b      The center of the bottom edge
5260 br     The bottom right corner
5261 </pre>
5262 Example Usage:
5263 <pre><code>
5264 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5265 el.alignTo("other-el");
5266
5267 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5268 el.alignTo("other-el", "tr?");
5269
5270 // align the bottom right corner of el with the center left edge of other-el
5271 el.alignTo("other-el", "br-l?");
5272
5273 // align the center of el with the bottom left corner of other-el and
5274 // adjust the x position by -6 pixels (and the y position by 0)
5275 el.alignTo("other-el", "c-bl", [-6, 0]);
5276 </code></pre>
5277      * @param {Mixed} element The element to align to.
5278      * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5279      * @param {Array} offsets (optional) Offset the positioning by [x, y]
5280      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5281      * @return {Ext.Element} this
5282      */
5283     alignTo : function(element, position, offsets, animate){
5284             var me = this;
5285         return me.setXY(me.getAlignToXY(element, position, offsets),
5286                                 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5287     },
5288     
5289     // private ==>  used outside of core
5290     adjustForConstraints : function(xy, parent, offsets){
5291         return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
5292     },
5293
5294     // private ==>  used outside of core
5295     getConstrainToXY : function(el, local, offsets, proposedXY){   
5296             var os = {top:0, left:0, bottom:0, right: 0};
5297
5298         return function(el, local, offsets, proposedXY){
5299             el = Ext.get(el);
5300             offsets = offsets ? Ext.applyIf(offsets, os) : os;
5301
5302             var vw, vh, vx = 0, vy = 0;
5303             if(el.dom == document.body || el.dom == document){
5304                 vw =Ext.lib.Dom.getViewWidth();
5305                 vh = Ext.lib.Dom.getViewHeight();
5306             }else{
5307                 vw = el.dom.clientWidth;
5308                 vh = el.dom.clientHeight;
5309                 if(!local){
5310                     var vxy = el.getXY();
5311                     vx = vxy[0];
5312                     vy = vxy[1];
5313                 }
5314             }
5315
5316             var s = el.getScroll();
5317
5318             vx += offsets.left + s.left;
5319             vy += offsets.top + s.top;
5320
5321             vw -= offsets.right;
5322             vh -= offsets.bottom;
5323
5324             var vr = vx+vw;
5325             var vb = vy+vh;
5326
5327             var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5328             var x = xy[0], y = xy[1];
5329             var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5330
5331             // only move it if it needs it
5332             var moved = false;
5333
5334             // first validate right/bottom
5335             if((x + w) > vr){
5336                 x = vr - w;
5337                 moved = true;
5338             }
5339             if((y + h) > vb){
5340                 y = vb - h;
5341                 moved = true;
5342             }
5343             // then make sure top/left isn't negative
5344             if(x < vx){
5345                 x = vx;
5346                 moved = true;
5347             }
5348             if(y < vy){
5349                 y = vy;
5350                 moved = true;
5351             }
5352             return moved ? [x, y] : false;
5353         };
5354     }(),
5355             
5356             
5357                 
5358 //         el = Ext.get(el);
5359 //         offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5360
5361 //         var  me = this,
5362 //              doc = document,
5363 //              s = el.getScroll(),
5364 //              vxy = el.getXY(),
5365 //              vx = offsets.left + s.left, 
5366 //              vy = offsets.top + s.top,               
5367 //              vw = -offsets.right, 
5368 //              vh = -offsets.bottom, 
5369 //              vr,
5370 //              vb,
5371 //              xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5372 //              x = xy[0],
5373 //              y = xy[1],
5374 //              w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5375 //              moved = false; // only move it if it needs it
5376 //       
5377 //              
5378 //         if(el.dom == doc.body || el.dom == doc){
5379 //             vw += Ext.lib.Dom.getViewWidth();
5380 //             vh += Ext.lib.Dom.getViewHeight();
5381 //         }else{
5382 //             vw += el.dom.clientWidth;
5383 //             vh += el.dom.clientHeight;
5384 //             if(!local){                    
5385 //                 vx += vxy[0];
5386 //                 vy += vxy[1];
5387 //             }
5388 //         }
5389
5390 //         // first validate right/bottom
5391 //         if(x + w > vx + vw){
5392 //             x = vx + vw - w;
5393 //             moved = true;
5394 //         }
5395 //         if(y + h > vy + vh){
5396 //             y = vy + vh - h;
5397 //             moved = true;
5398 //         }
5399 //         // then make sure top/left isn't negative
5400 //         if(x < vx){
5401 //             x = vx;
5402 //             moved = true;
5403 //         }
5404 //         if(y < vy){
5405 //             y = vy;
5406 //             moved = true;
5407 //         }
5408 //         return moved ? [x, y] : false;
5409 //    },
5410     
5411     /**
5412     * Calculates the x, y to center this element on the screen
5413     * @return {Array} The x, y values [x, y]
5414     */
5415     getCenterXY : function(){
5416         return this.getAlignToXY(document, 'c-c');
5417     },
5418
5419     /**
5420     * Centers the Element in either the viewport, or another Element.
5421     * @param {Mixed} centerIn (optional) The element in which to center the element.
5422     */
5423     center : function(centerIn){
5424         return this.alignTo(centerIn || document, 'c-c');        
5425     }    
5426 });
5427 /**
5428  * @class Ext.Element
5429  */
5430 Ext.Element.addMethods(function(){
5431         var PARENTNODE = 'parentNode',
5432                 NEXTSIBLING = 'nextSibling',
5433                 PREVIOUSSIBLING = 'previousSibling',
5434                 DQ = Ext.DomQuery,
5435                 GET = Ext.get;
5436         
5437         return {
5438                 /**
5439              * 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)
5440              * @param {String} selector The simple selector to test
5441              * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5442              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5443              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5444              */
5445             findParent : function(simpleSelector, maxDepth, returnEl){
5446                 var p = this.dom,
5447                         b = document.body, 
5448                         depth = 0,                      
5449                         stopEl;         
5450             if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5451                 return null;
5452             }
5453                 maxDepth = maxDepth || 50;
5454                 if (isNaN(maxDepth)) {
5455                     stopEl = Ext.getDom(maxDepth);
5456                     maxDepth = Number.MAX_VALUE;
5457                 }
5458                 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5459                     if(DQ.is(p, simpleSelector)){
5460                         return returnEl ? GET(p) : p;
5461                     }
5462                     depth++;
5463                     p = p.parentNode;
5464                 }
5465                 return null;
5466             },
5467         
5468             /**
5469              * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5470              * @param {String} selector The simple selector to test
5471              * @param {Number/Mixed} maxDepth (optional) The max depth to
5472                     search as a number or element (defaults to 10 || document.body)
5473              * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5474              * @return {HTMLElement} The matching DOM node (or null if no match was found)
5475              */
5476             findParentNode : function(simpleSelector, maxDepth, returnEl){
5477                 var p = Ext.fly(this.dom.parentNode, '_internal');
5478                 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5479             },
5480         
5481             /**
5482              * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5483              * This is a shortcut for findParentNode() that always returns an Ext.Element.
5484              * @param {String} selector The simple selector to test
5485              * @param {Number/Mixed} maxDepth (optional) The max depth to
5486                     search as a number or element (defaults to 10 || document.body)
5487              * @return {Ext.Element} The matching DOM node (or null if no match was found)
5488              */
5489             up : function(simpleSelector, maxDepth){
5490                 return this.findParentNode(simpleSelector, maxDepth, true);
5491             },
5492         
5493             /**
5494              * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5495              * @param {String} selector The CSS selector
5496              * @return {CompositeElement/CompositeElementLite} The composite element
5497              */
5498             select : function(selector){
5499                 return Ext.Element.select(selector, this.dom);
5500             },
5501         
5502             /**
5503              * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5504              * @param {String} selector The CSS selector
5505              * @return {Array} An array of the matched nodes
5506              */
5507             query : function(selector){
5508                 return DQ.select(selector, this.dom);
5509             },
5510         
5511             /**
5512              * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5513              * @param {String} selector The CSS selector
5514              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5515              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5516              */
5517             child : function(selector, returnDom){
5518                 var n = DQ.selectNode(selector, this.dom);
5519                 return returnDom ? n : GET(n);
5520             },
5521         
5522             /**
5523              * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5524              * @param {String} selector The CSS selector
5525              * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5526              * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5527              */
5528             down : function(selector, returnDom){
5529                 var n = DQ.selectNode(" > " + selector, this.dom);
5530                 return returnDom ? n : GET(n);
5531             },
5532         
5533                  /**
5534              * Gets the parent node for this element, optionally chaining up trying to match a selector
5535              * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5536              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5537              * @return {Ext.Element/HTMLElement} The parent node or null
5538                  */
5539             parent : function(selector, returnDom){
5540                 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5541             },
5542         
5543              /**
5544              * Gets the next sibling, skipping text nodes
5545              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5546              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5547              * @return {Ext.Element/HTMLElement} The next sibling or null
5548                  */
5549             next : function(selector, returnDom){
5550                 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5551             },
5552         
5553             /**
5554              * Gets the previous sibling, skipping text nodes
5555              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5556              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5557              * @return {Ext.Element/HTMLElement} The previous sibling or null
5558                  */
5559             prev : function(selector, returnDom){
5560                 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5561             },
5562         
5563         
5564             /**
5565              * Gets the first child, skipping text nodes
5566              * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5567              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5568              * @return {Ext.Element/HTMLElement} The first child or null
5569                  */
5570             first : function(selector, returnDom){
5571                 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5572             },
5573         
5574             /**
5575              * Gets the last child, skipping text nodes
5576              * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5577              * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5578              * @return {Ext.Element/HTMLElement} The last child or null
5579                  */
5580             last : function(selector, returnDom){
5581                 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5582             },
5583             
5584             matchNode : function(dir, start, selector, returnDom){
5585                 var n = this.dom[start];
5586                 while(n){
5587                     if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5588                         return !returnDom ? GET(n) : n;
5589                     }
5590                     n = n[dir];
5591                 }
5592                 return null;
5593             }   
5594     }
5595 }());/**
5596  * @class Ext.Element
5597  */
5598 Ext.Element.addMethods({
5599     /**
5600      * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5601      * @param {String} selector The CSS selector
5602      * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5603      * @return {CompositeElement/CompositeElementLite} The composite element
5604      */
5605     select : function(selector, unique){
5606         return Ext.Element.select(selector, unique, this.dom);
5607     }
5608 });/**
5609  * @class Ext.Element
5610  */
5611 Ext.Element.addMethods(
5612 function() {
5613         var GETDOM = Ext.getDom,
5614                 GET = Ext.get,
5615                 DH = Ext.DomHelper;
5616         
5617         return {
5618             /**
5619              * Appends the passed element(s) to this element
5620              * @param {String/HTMLElement/Array/Element/CompositeElement} el
5621              * @return {Ext.Element} this
5622              */
5623             appendChild: function(el){        
5624                 return GET(el).appendTo(this);        
5625             },
5626         
5627             /**
5628              * Appends this element to the passed element
5629              * @param {Mixed} el The new parent element
5630              * @return {Ext.Element} this
5631              */
5632             appendTo: function(el){        
5633                 GETDOM(el).appendChild(this.dom);        
5634                 return this;
5635             },
5636         
5637             /**
5638              * Inserts this element before the passed element in the DOM
5639              * @param {Mixed} el The element before which this element will be inserted
5640              * @return {Ext.Element} this
5641              */
5642             insertBefore: function(el){                   
5643                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5644                 return this;
5645             },
5646         
5647             /**
5648              * Inserts this element after the passed element in the DOM
5649              * @param {Mixed} el The element to insert after
5650              * @return {Ext.Element} this
5651              */
5652             insertAfter: function(el){
5653                 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5654                 return this;
5655             },
5656         
5657             /**
5658              * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5659              * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5660              * @return {Ext.Element} The new child
5661              */
5662             insertFirst: function(el, returnDom){
5663             el = el || {};
5664             if(el.nodeType || el.dom || typeof el == 'string'){ // element
5665                 el = GETDOM(el);
5666                 this.dom.insertBefore(el, this.dom.firstChild);
5667                 return !returnDom ? GET(el) : el;
5668             }else{ // dh config
5669                 return this.createChild(el, this.dom.firstChild, returnDom);
5670             }
5671         },
5672         
5673             /**
5674              * Replaces the passed element with this element
5675              * @param {Mixed} el The element to replace
5676              * @return {Ext.Element} this
5677              */
5678             replace: function(el){
5679                 el = GET(el);
5680                 this.insertBefore(el);
5681                 el.remove();
5682                 return this;
5683             },
5684         
5685             /**
5686              * Replaces this element with the passed element
5687              * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5688              * @return {Ext.Element} this
5689              */
5690             replaceWith: function(el){
5691                     var me = this;
5692                 
5693             if(el.nodeType || el.dom || typeof el == 'string'){
5694                 el = GETDOM(el);
5695                 me.dom.parentNode.insertBefore(el, me.dom);
5696             }else{
5697                 el = DH.insertBefore(me.dom, el);
5698             }
5699                 
5700                 delete Ext.elCache[me.id];
5701                 Ext.removeNode(me.dom);      
5702                 me.id = Ext.id(me.dom = el);
5703                 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
5704             return me;
5705             },
5706             
5707                 /**
5708                  * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5709                  * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
5710                  * automatically generated with the specified attributes.
5711                  * @param {HTMLElement} insertBefore (optional) a child element of this element
5712                  * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5713                  * @return {Ext.Element} The new child element
5714                  */
5715                 createChild: function(config, insertBefore, returnDom){
5716                     config = config || {tag:'div'};
5717                     return insertBefore ? 
5718                            DH.insertBefore(insertBefore, config, returnDom !== true) :  
5719                            DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
5720                 },
5721                 
5722                 /**
5723                  * Creates and wraps this element with another element
5724                  * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5725                  * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5726                  * @return {HTMLElement/Element} The newly created wrapper element
5727                  */
5728                 wrap: function(config, returnDom){        
5729                     var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5730                     newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5731                     return newEl;
5732                 },
5733                 
5734                 /**
5735                  * Inserts an html fragment into this element
5736                  * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5737                  * @param {String} html The HTML fragment
5738                  * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5739                  * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5740                  */
5741                 insertHtml : function(where, html, returnEl){
5742                     var el = DH.insertHtml(where, this.dom, html);
5743                     return returnEl ? Ext.get(el) : el;
5744                 }
5745         }
5746 }());/**
5747  * @class Ext.Element
5748  */
5749 Ext.apply(Ext.Element.prototype, function() {
5750         var GETDOM = Ext.getDom,
5751                 GET = Ext.get,
5752                 DH = Ext.DomHelper;
5753         
5754         return {        
5755                 /**
5756              * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5757              * @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.
5758              * @param {String} where (optional) 'before' or 'after' defaults to before
5759              * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5760              * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5761              */
5762             insertSibling: function(el, where, returnDom){
5763                 var me = this,
5764                         rt,
5765                 isAfter = (where || 'before').toLowerCase() == 'after',
5766                 insertEl;
5767                         
5768                 if(Ext.isArray(el)){
5769                 insertEl = me;
5770                     Ext.each(el, function(e) {
5771                             rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5772                     if(isAfter){
5773                         insertEl = rt;
5774                     }
5775                     });
5776                     return rt;
5777                 }
5778                         
5779                 el = el || {};
5780                 
5781             if(el.nodeType || el.dom){
5782                 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5783                 if (!returnDom) {
5784                     rt = GET(rt);
5785                 }
5786             }else{
5787                 if (isAfter && !me.dom.nextSibling) {
5788                     rt = DH.append(me.dom.parentNode, el, !returnDom);
5789                 } else {                    
5790                     rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5791                 }
5792             }
5793                 return rt;
5794             }
5795     };
5796 }());/**
5797  * @class Ext.Element
5798  */
5799 Ext.Element.addMethods(function(){
5800     // local style camelizing for speed
5801     var propCache = {},
5802         camelRe = /(-[a-z])/gi,
5803         classReCache = {},
5804         view = document.defaultView,
5805         propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5806         opacityRe = /alpha\(opacity=(.*)\)/i,
5807         trimRe = /^\s+|\s+$/g,
5808         spacesRe = /\s+/,
5809         wordsRe = /\w/g,
5810         EL = Ext.Element,
5811         PADDING = "padding",
5812         MARGIN = "margin",
5813         BORDER = "border",
5814         LEFT = "-left",
5815         RIGHT = "-right",
5816         TOP = "-top",
5817         BOTTOM = "-bottom",
5818         WIDTH = "-width",
5819         MATH = Math,
5820         HIDDEN = 'hidden',
5821         ISCLIPPED = 'isClipped',
5822         OVERFLOW = 'overflow',
5823         OVERFLOWX = 'overflow-x',
5824         OVERFLOWY = 'overflow-y',
5825         ORIGINALCLIP = 'originalClip',
5826         // special markup used throughout Ext when box wrapping elements
5827         borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5828         paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5829         margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5830         data = Ext.Element.data;
5831
5832
5833     // private
5834     function camelFn(m, a) {
5835         return a.charAt(1).toUpperCase();
5836     }
5837
5838     function chkCache(prop) {
5839         return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5840     }
5841
5842     return {
5843         // private  ==> used by Fx
5844         adjustWidth : function(width) {
5845             var me = this;
5846             var isNum = (typeof width == "number");
5847             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5848                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5849             }
5850             return (isNum && width < 0) ? 0 : width;
5851         },
5852
5853         // private   ==> used by Fx
5854         adjustHeight : function(height) {
5855             var me = this;
5856             var isNum = (typeof height == "number");
5857             if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5858                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5859             }
5860             return (isNum && height < 0) ? 0 : height;
5861         },
5862
5863
5864         /**
5865          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5866          * @param {String/Array} className The CSS class to add, or an array of classes
5867          * @return {Ext.Element} this
5868          */
5869         addClass : function(className){
5870             var me = this,
5871                 i,
5872                 len,
5873                 v,
5874                 cls = [];
5875             // Separate case is for speed
5876             if (!Ext.isArray(className)) {
5877                 if (typeof className == 'string' && !this.hasClass(className)) {
5878                     me.dom.className += " " + className;
5879                 }
5880             }
5881             else {
5882                 for (i = 0, len = className.length; i < len; i++) {
5883                     v = className[i];
5884                     if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
5885                         cls.push(v);
5886                     }
5887                 }
5888                 if (cls.length) {
5889                     me.dom.className += " " + cls.join(" ");
5890                 }
5891             }
5892             return me;
5893         },
5894
5895         /**
5896          * Removes one or more CSS classes from the element.
5897          * @param {String/Array} className The CSS class to remove, or an array of classes
5898          * @return {Ext.Element} this
5899          */
5900         removeClass : function(className){
5901             var me = this,
5902                 i,
5903                 idx,
5904                 len,
5905                 cls,
5906                 elClasses;
5907             if (!Ext.isArray(className)){
5908                 className = [className];
5909             }
5910             if (me.dom && me.dom.className) {
5911                 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
5912                 for (i = 0, len = className.length; i < len; i++) {
5913                     cls = className[i];
5914                     if (typeof cls == 'string') {
5915                         cls = cls.replace(trimRe, '');
5916                         idx = elClasses.indexOf(cls);
5917                         if (idx != -1) {
5918                             elClasses.splice(idx, 1);
5919                         }
5920                     }
5921                 }
5922                 me.dom.className = elClasses.join(" ");
5923             }
5924             return me;
5925         },
5926
5927         /**
5928          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5929          * @param {String/Array} className The CSS class to add, or an array of classes
5930          * @return {Ext.Element} this
5931          */
5932         radioClass : function(className){
5933             var cn = this.dom.parentNode.childNodes,
5934                 v,
5935                 i,
5936                 len;
5937             className = Ext.isArray(className) ? className : [className];
5938             for (i = 0, len = cn.length; i < len; i++) {
5939                 v = cn[i];
5940                 if (v && v.nodeType == 1) {
5941                     Ext.fly(v, '_internal').removeClass(className);
5942                 }
5943             };
5944             return this.addClass(className);
5945         },
5946
5947         /**
5948          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5949          * @param {String} className The CSS class to toggle
5950          * @return {Ext.Element} this
5951          */
5952         toggleClass : function(className){
5953             return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5954         },
5955
5956         /**
5957          * Checks if the specified CSS class exists on this element's DOM node.
5958          * @param {String} className The CSS class to check for
5959          * @return {Boolean} True if the class exists, else false
5960          */
5961         hasClass : function(className){
5962             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5963         },
5964
5965         /**
5966          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
5967          * @param {String} oldClassName The CSS class to replace
5968          * @param {String} newClassName The replacement CSS class
5969          * @return {Ext.Element} this
5970          */
5971         replaceClass : function(oldClassName, newClassName){
5972             return this.removeClass(oldClassName).addClass(newClassName);
5973         },
5974
5975         isStyle : function(style, val) {
5976             return this.getStyle(style) == val;
5977         },
5978
5979         /**
5980          * Normalizes currentStyle and computedStyle.
5981          * @param {String} property The style property whose value is returned.
5982          * @return {String} The current value of the style property for this element.
5983          */
5984         getStyle : function(){
5985             return view && view.getComputedStyle ?
5986                 function(prop){
5987                     var el = this.dom,
5988                         v,
5989                         cs,
5990                         out,
5991                         display,
5992                         wk = Ext.isWebKit,
5993                         display;
5994
5995                     if(el == document){
5996                         return null;
5997                     }
5998                     prop = chkCache(prop);
5999                     // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
6000                     if(wk && /marginRight/.test(prop)){
6001                         display = this.getStyle('display');
6002                         el.style.display = 'inline-block';
6003                     }
6004                     out = (v = el.style[prop]) ? v :
6005                            (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
6006
6007                     // Webkit returns rgb values for transparent.
6008                     if(wk){
6009                         if(out == 'rgba(0, 0, 0, 0)'){
6010                             out = 'transparent';
6011                         }else if(display){
6012                             el.style.display = display;
6013                         }
6014                     }
6015                     return out;
6016                 } :
6017                 function(prop){
6018                     var el = this.dom,
6019                         m,
6020                         cs;
6021
6022                     if(el == document) return null;
6023                     if (prop == 'opacity') {
6024                         if (el.style.filter.match) {
6025                             if(m = el.style.filter.match(opacityRe)){
6026                                 var fv = parseFloat(m[1]);
6027                                 if(!isNaN(fv)){
6028                                     return fv ? fv / 100 : 0;
6029                                 }
6030                             }
6031                         }
6032                         return 1;
6033                     }
6034                     prop = chkCache(prop);
6035                     return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
6036                 };
6037         }(),
6038
6039         /**
6040          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
6041          * are convert to standard 6 digit hex color.
6042          * @param {String} attr The css attribute
6043          * @param {String} defaultValue The default value to use when a valid color isn't found
6044          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
6045          * color anims.
6046          */
6047         getColor : function(attr, defaultValue, prefix){
6048             var v = this.getStyle(attr),
6049                 color = (typeof prefix != 'undefined') ? prefix : '#',
6050                 h;
6051
6052             if(!v || /transparent|inherit/.test(v)){
6053                 return defaultValue;
6054             }
6055             if(/^r/.test(v)){
6056                 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6057                     h = parseInt(s, 10);
6058                     color += (h < 16 ? '0' : '') + h.toString(16);
6059                 });
6060             }else{
6061                 v = v.replace('#', '');
6062                 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6063             }
6064             return(color.length > 5 ? color.toLowerCase() : defaultValue);
6065         },
6066
6067         /**
6068          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6069          * @param {String/Object} property The style property to be set, or an object of multiple styles.
6070          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6071          * @return {Ext.Element} this
6072          */
6073         setStyle : function(prop, value){
6074             var tmp,
6075                 style,
6076                 camel;
6077             if (typeof prop != 'object') {
6078                 tmp = {};
6079                 tmp[prop] = value;
6080                 prop = tmp;
6081             }
6082             for (style in prop) {
6083                 value = prop[style];
6084                 style == 'opacity' ?
6085                     this.setOpacity(value) :
6086                     this.dom.style[chkCache(style)] = value;
6087             }
6088             return this;
6089         },
6090
6091         /**
6092          * Set the opacity of the element
6093          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6094          * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6095          * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6096          * @return {Ext.Element} this
6097          */
6098          setOpacity : function(opacity, animate){
6099             var me = this,
6100                 s = me.dom.style;
6101
6102             if(!animate || !me.anim){
6103                 if(Ext.isIE){
6104                     var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6105                     val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6106
6107                     s.zoom = 1;
6108                     s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6109                 }else{
6110                     s.opacity = opacity;
6111                 }
6112             }else{
6113                 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6114             }
6115             return me;
6116         },
6117
6118         /**
6119          * Clears any opacity settings from this element. Required in some cases for IE.
6120          * @return {Ext.Element} this
6121          */
6122         clearOpacity : function(){
6123             var style = this.dom.style;
6124             if(Ext.isIE){
6125                 if(!Ext.isEmpty(style.filter)){
6126                     style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6127                 }
6128             }else{
6129                 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6130             }
6131             return this;
6132         },
6133
6134         /**
6135          * Returns the offset height of the element
6136          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6137          * @return {Number} The element's height
6138          */
6139         getHeight : function(contentHeight){
6140             var me = this,
6141                 dom = me.dom,
6142                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6143                 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6144
6145             h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6146             return h < 0 ? 0 : h;
6147         },
6148
6149         /**
6150          * Returns the offset width of the element
6151          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6152          * @return {Number} The element's width
6153          */
6154         getWidth : function(contentWidth){
6155             var me = this,
6156                 dom = me.dom,
6157                 hidden = Ext.isIE && me.isStyle('display', 'none'),
6158                 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6159             w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6160             return w < 0 ? 0 : w;
6161         },
6162
6163         /**
6164          * Set the width of this Element.
6165          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6166          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6167          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6168          * </ul></div>
6169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6170          * @return {Ext.Element} this
6171          */
6172         setWidth : function(width, animate){
6173             var me = this;
6174             width = me.adjustWidth(width);
6175             !animate || !me.anim ?
6176                 me.dom.style.width = me.addUnits(width) :
6177                 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6178             return me;
6179         },
6180
6181         /**
6182          * Set the height of this Element.
6183          * <pre><code>
6184 // change the height to 200px and animate with default configuration
6185 Ext.fly('elementId').setHeight(200, true);
6186
6187 // change the height to 150px and animate with a custom configuration
6188 Ext.fly('elId').setHeight(150, {
6189     duration : .5, // animation will have a duration of .5 seconds
6190     // will change the content to "finished"
6191     callback: function(){ this.{@link #update}("finished"); }
6192 });
6193          * </code></pre>
6194          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6195          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6196          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6197          * </ul></div>
6198          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6199          * @return {Ext.Element} this
6200          */
6201          setHeight : function(height, animate){
6202             var me = this;
6203             height = me.adjustHeight(height);
6204             !animate || !me.anim ?
6205                 me.dom.style.height = me.addUnits(height) :
6206                 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6207             return me;
6208         },
6209
6210         /**
6211          * Gets the width of the border(s) for the specified side(s)
6212          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6213          * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6214          * @return {Number} The width of the sides passed added together
6215          */
6216         getBorderWidth : function(side){
6217             return this.addStyles(side, borders);
6218         },
6219
6220         /**
6221          * Gets the width of the padding(s) for the specified side(s)
6222          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6223          * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6224          * @return {Number} The padding of the sides passed added together
6225          */
6226         getPadding : function(side){
6227             return this.addStyles(side, paddings);
6228         },
6229
6230         /**
6231          *  Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6232          * @return {Ext.Element} this
6233          */
6234         clip : function(){
6235             var me = this,
6236                 dom = me.dom;
6237
6238             if(!data(dom, ISCLIPPED)){
6239                 data(dom, ISCLIPPED, true);
6240                 data(dom, ORIGINALCLIP, {
6241                     o: me.getStyle(OVERFLOW),
6242                     x: me.getStyle(OVERFLOWX),
6243                     y: me.getStyle(OVERFLOWY)
6244                 });
6245                 me.setStyle(OVERFLOW, HIDDEN);
6246                 me.setStyle(OVERFLOWX, HIDDEN);
6247                 me.setStyle(OVERFLOWY, HIDDEN);
6248             }
6249             return me;
6250         },
6251
6252         /**
6253          *  Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6254          * @return {Ext.Element} this
6255          */
6256         unclip : function(){
6257             var me = this,
6258                 dom = me.dom;
6259
6260             if(data(dom, ISCLIPPED)){
6261                 data(dom, ISCLIPPED, false);
6262                 var o = data(dom, ORIGINALCLIP);
6263                 if(o.o){
6264                     me.setStyle(OVERFLOW, o.o);
6265                 }
6266                 if(o.x){
6267                     me.setStyle(OVERFLOWX, o.x);
6268                 }
6269                 if(o.y){
6270                     me.setStyle(OVERFLOWY, o.y);
6271                 }
6272             }
6273             return me;
6274         },
6275
6276         // private
6277         addStyles : function(sides, styles){
6278             var ttlSize = 0,
6279                 sidesArr = sides.match(wordsRe),
6280                 side,
6281                 size,
6282                 i,
6283                 len = sidesArr.length;
6284             for (i = 0; i < len; i++) {
6285                 side = sidesArr[i];
6286                 size = side && parseInt(this.getStyle(styles[side]), 10);
6287                 if (size) {
6288                     ttlSize += MATH.abs(size);
6289                 }
6290             }
6291             return ttlSize;
6292         },
6293
6294         margins : margins
6295     }
6296 }()
6297 );
6298 /**
6299  * @class Ext.Element
6300  */
6301
6302 // special markup used throughout Ext when box wrapping elements
6303 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>';
6304
6305 Ext.Element.addMethods(function(){
6306     var INTERNAL = "_internal",
6307         pxMatch = /(\d+\.?\d+)px/;
6308     return {
6309         /**
6310          * More flexible version of {@link #setStyle} for setting style properties.
6311          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6312          * a function which returns such a specification.
6313          * @return {Ext.Element} this
6314          */
6315         applyStyles : function(style){
6316             Ext.DomHelper.applyStyles(this.dom, style);
6317             return this;
6318         },
6319
6320         /**
6321          * Returns an object with properties matching the styles requested.
6322          * For example, el.getStyles('color', 'font-size', 'width') might return
6323          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6324          * @param {String} style1 A style name
6325          * @param {String} style2 A style name
6326          * @param {String} etc.
6327          * @return {Object} The style object
6328          */
6329         getStyles : function(){
6330             var ret = {};
6331             Ext.each(arguments, function(v) {
6332                ret[v] = this.getStyle(v);
6333             },
6334             this);
6335             return ret;
6336         },
6337
6338         // private  ==> used by ext full
6339         setOverflow : function(v){
6340             var dom = this.dom;
6341             if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6342                 dom.style.overflow = 'hidden';
6343                 (function(){dom.style.overflow = 'auto';}).defer(1);
6344             }else{
6345                 dom.style.overflow = v;
6346             }
6347         },
6348
6349        /**
6350         * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6351         * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6352         * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6353         * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}).  The markup
6354         * is of this form:</p>
6355         * <pre><code>
6356     Ext.Element.boxMarkup =
6357     &#39;&lt;div class="{0}-tl">&lt;div class="{0}-tr">&lt;div class="{0}-tc">&lt;/div>&lt;/div>&lt;/div>
6358      &lt;div class="{0}-ml">&lt;div class="{0}-mr">&lt;div class="{0}-mc">&lt;/div>&lt;/div>&lt;/div>
6359      &lt;div class="{0}-bl">&lt;div class="{0}-br">&lt;div class="{0}-bc">&lt;/div>&lt;/div>&lt;/div>&#39;;
6360         * </code></pre>
6361         * <p>Example usage:</p>
6362         * <pre><code>
6363     // Basic box wrap
6364     Ext.get("foo").boxWrap();
6365
6366     // You can also add a custom class and use CSS inheritance rules to customize the box look.
6367     // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6368     // for how to create a custom box wrap style.
6369     Ext.get("foo").boxWrap().addClass("x-box-blue");
6370         * </code></pre>
6371         * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6372         * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6373         * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6374         * also supply all of the necessary rules.
6375         * @return {Ext.Element} The outermost wrapping element of the created box structure.
6376         */
6377         boxWrap : function(cls){
6378             cls = cls || 'x-box';
6379             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)));
6380             Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6381             return el;
6382         },
6383
6384         /**
6385          * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6386          * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6387          * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6388          * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6389          * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6390          * </ul></div>
6391          * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6392          * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6393          * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6394          * </ul></div>
6395          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6396          * @return {Ext.Element} this
6397          */
6398         setSize : function(width, height, animate){
6399             var me = this;
6400             if(typeof width == 'object'){ // in case of object from getSize()
6401                 height = width.height;
6402                 width = width.width;
6403             }
6404             width = me.adjustWidth(width);
6405             height = me.adjustHeight(height);
6406             if(!animate || !me.anim){
6407                 me.dom.style.width = me.addUnits(width);
6408                 me.dom.style.height = me.addUnits(height);
6409             }else{
6410                 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6411             }
6412             return me;
6413         },
6414
6415         /**
6416          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6417          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6418          * if a height has not been set using CSS.
6419          * @return {Number}
6420          */
6421         getComputedHeight : function(){
6422             var me = this,
6423                 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6424             if(!h){
6425                 h = parseFloat(me.getStyle('height')) || 0;
6426                 if(!me.isBorderBox()){
6427                     h += me.getFrameWidth('tb');
6428                 }
6429             }
6430             return h;
6431         },
6432
6433         /**
6434          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6435          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6436          * if a width has not been set using CSS.
6437          * @return {Number}
6438          */
6439         getComputedWidth : function(){
6440             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6441             if(!w){
6442                 w = parseFloat(this.getStyle('width')) || 0;
6443                 if(!this.isBorderBox()){
6444                     w += this.getFrameWidth('lr');
6445                 }
6446             }
6447             return w;
6448         },
6449
6450         /**
6451          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6452          for more information about the sides.
6453          * @param {String} sides
6454          * @return {Number}
6455          */
6456         getFrameWidth : function(sides, onlyContentBox){
6457             return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6458         },
6459
6460         /**
6461          * Sets up event handlers to add and remove a css class when the mouse is over this element
6462          * @param {String} className
6463          * @return {Ext.Element} this
6464          */
6465         addClassOnOver : function(className){
6466             this.hover(
6467                 function(){
6468                     Ext.fly(this, INTERNAL).addClass(className);
6469                 },
6470                 function(){
6471                     Ext.fly(this, INTERNAL).removeClass(className);
6472                 }
6473             );
6474             return this;
6475         },
6476
6477         /**
6478          * Sets up event handlers to add and remove a css class when this element has the focus
6479          * @param {String} className
6480          * @return {Ext.Element} this
6481          */
6482         addClassOnFocus : function(className){
6483             this.on("focus", function(){
6484                 Ext.fly(this, INTERNAL).addClass(className);
6485             }, this.dom);
6486             this.on("blur", function(){
6487                 Ext.fly(this, INTERNAL).removeClass(className);
6488             }, this.dom);
6489             return this;
6490         },
6491
6492         /**
6493          * 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)
6494          * @param {String} className
6495          * @return {Ext.Element} this
6496          */
6497         addClassOnClick : function(className){
6498             var dom = this.dom;
6499             this.on("mousedown", function(){
6500                 Ext.fly(dom, INTERNAL).addClass(className);
6501                 var d = Ext.getDoc(),
6502                     fn = function(){
6503                         Ext.fly(dom, INTERNAL).removeClass(className);
6504                         d.removeListener("mouseup", fn);
6505                     };
6506                 d.on("mouseup", fn);
6507             });
6508             return this;
6509         },
6510
6511         /**
6512          * <p>Returns the dimensions of the element available to lay content out in.<p>
6513          * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6514          * example:<pre><code>
6515         var vpSize = Ext.getBody().getViewSize();
6516
6517         // all Windows created afterwards will have a default value of 90% height and 95% width
6518         Ext.Window.override({
6519             width: vpSize.width * 0.9,
6520             height: vpSize.height * 0.95
6521         });
6522         // To handle window resizing you would have to hook onto onWindowResize.
6523         * </code></pre>
6524         *
6525         * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6526         * To obtain the size including scrollbars, use getStyleSize
6527         *
6528         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6529         */
6530
6531         getViewSize : function(){
6532             var doc = document,
6533                 d = this.dom,
6534                 isDoc = (d == doc || d == doc.body);
6535
6536             // If the body, use Ext.lib.Dom
6537             if (isDoc) {
6538                 var extdom = Ext.lib.Dom;
6539                 return {
6540                     width : extdom.getViewWidth(),
6541                     height : extdom.getViewHeight()
6542                 };
6543
6544             // Else use clientHeight/clientWidth
6545             } else {
6546                 return {
6547                     width : d.clientWidth,
6548                     height : d.clientHeight
6549                 }
6550             }
6551         },
6552
6553         /**
6554         * <p>Returns the dimensions of the element available to lay content out in.<p>
6555         *
6556         * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6557         * To obtain the size excluding scrollbars, use getViewSize
6558         *
6559         * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6560         */
6561
6562         getStyleSize : function(){
6563             var me = this,
6564                 w, h,
6565                 doc = document,
6566                 d = this.dom,
6567                 isDoc = (d == doc || d == doc.body),
6568                 s = d.style;
6569
6570             // If the body, use Ext.lib.Dom
6571             if (isDoc) {
6572                 var extdom = Ext.lib.Dom;
6573                 return {
6574                     width : extdom.getViewWidth(),
6575                     height : extdom.getViewHeight()
6576                 }
6577             }
6578             // Use Styles if they are set
6579             if(s.width && s.width != 'auto'){
6580                 w = parseFloat(s.width);
6581                 if(me.isBorderBox()){
6582                    w -= me.getFrameWidth('lr');
6583                 }
6584             }
6585             // Use Styles if they are set
6586             if(s.height && s.height != 'auto'){
6587                 h = parseFloat(s.height);
6588                 if(me.isBorderBox()){
6589                    h -= me.getFrameWidth('tb');
6590                 }
6591             }
6592             // Use getWidth/getHeight if style not set.
6593             return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6594         },
6595
6596         /**
6597          * Returns the size of the element.
6598          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6599          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6600          */
6601         getSize : function(contentSize){
6602             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6603         },
6604
6605         /**
6606          * Forces the browser to repaint this element
6607          * @return {Ext.Element} this
6608          */
6609         repaint : function(){
6610             var dom = this.dom;
6611             this.addClass("x-repaint");
6612             setTimeout(function(){
6613                 Ext.fly(dom).removeClass("x-repaint");
6614             }, 1);
6615             return this;
6616         },
6617
6618         /**
6619          * Disables text selection for this element (normalized across browsers)
6620          * @return {Ext.Element} this
6621          */
6622         unselectable : function(){
6623             this.dom.unselectable = "on";
6624             return this.swallowEvent("selectstart", true).
6625                         applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6626                         addClass("x-unselectable");
6627         },
6628
6629         /**
6630          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6631          * then it returns the calculated width of the sides (see getPadding)
6632          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6633          * @return {Object/Number}
6634          */
6635         getMargins : function(side){
6636             var me = this,
6637                 key,
6638                 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6639                 o = {};
6640
6641             if (!side) {
6642                 for (key in me.margins){
6643                     o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6644                 }
6645                 return o;
6646             } else {
6647                 return me.addStyles.call(me, side, me.margins);
6648             }
6649         }
6650     };
6651 }());
6652 /**
6653  * @class Ext.Element
6654  */
6655 (function(){
6656 var D = Ext.lib.Dom,
6657         LEFT = "left",
6658         RIGHT = "right",
6659         TOP = "top",
6660         BOTTOM = "bottom",
6661         POSITION = "position",
6662         STATIC = "static",
6663         RELATIVE = "relative",
6664         AUTO = "auto",
6665         ZINDEX = "z-index";
6666
6667 Ext.Element.addMethods({
6668         /**
6669       * 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).
6670       * @return {Number} The X position of the element
6671       */
6672     getX : function(){
6673         return D.getX(this.dom);
6674     },
6675
6676     /**
6677       * 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).
6678       * @return {Number} The Y position of the element
6679       */
6680     getY : function(){
6681         return D.getY(this.dom);
6682     },
6683
6684     /**
6685       * 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).
6686       * @return {Array} The XY position of the element
6687       */
6688     getXY : function(){
6689         return D.getXY(this.dom);
6690     },
6691
6692     /**
6693       * 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.
6694       * @param {Mixed} element The element to get the offsets from.
6695       * @return {Array} The XY page offsets (e.g. [100, -200])
6696       */
6697     getOffsetsTo : function(el){
6698         var o = this.getXY(),
6699                 e = Ext.fly(el, '_internal').getXY();
6700         return [o[0]-e[0],o[1]-e[1]];
6701     },
6702
6703     /**
6704      * 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).
6705      * @param {Number} The X position of the element
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     setX : function(x, animate){            
6710             return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6711     },
6712
6713     /**
6714      * 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).
6715      * @param {Number} The Y position of the element
6716      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6717      * @return {Ext.Element} this
6718      */
6719     setY : function(y, animate){            
6720             return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6721     },
6722
6723     /**
6724      * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6725      * @param {String} left The left CSS property value
6726      * @return {Ext.Element} this
6727      */
6728     setLeft : function(left){
6729         this.setStyle(LEFT, this.addUnits(left));
6730         return this;
6731     },
6732
6733     /**
6734      * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6735      * @param {String} top The top CSS property value
6736      * @return {Ext.Element} this
6737      */
6738     setTop : function(top){
6739         this.setStyle(TOP, this.addUnits(top));
6740         return this;
6741     },
6742
6743     /**
6744      * Sets the element's CSS right style.
6745      * @param {String} right The right CSS property value
6746      * @return {Ext.Element} this
6747      */
6748     setRight : function(right){
6749         this.setStyle(RIGHT, this.addUnits(right));
6750         return this;
6751     },
6752
6753     /**
6754      * Sets the element's CSS bottom style.
6755      * @param {String} bottom The bottom CSS property value
6756      * @return {Ext.Element} this
6757      */
6758     setBottom : function(bottom){
6759         this.setStyle(BOTTOM, this.addUnits(bottom));
6760         return this;
6761     },
6762
6763     /**
6764      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6765      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6766      * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6767      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6768      * @return {Ext.Element} this
6769      */
6770     setXY : function(pos, animate){
6771             var me = this;
6772         if(!animate || !me.anim){
6773             D.setXY(me.dom, pos);
6774         }else{
6775             me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6776         }
6777         return me;
6778     },
6779
6780     /**
6781      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6782      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6783      * @param {Number} x X value for new position (coordinates are page-based)
6784      * @param {Number} y Y value for new position (coordinates are page-based)
6785      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6786      * @return {Ext.Element} this
6787      */
6788     setLocation : function(x, y, animate){
6789         return this.setXY([x, y], this.animTest(arguments, animate, 2));
6790     },
6791
6792     /**
6793      * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6794      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6795      * @param {Number} x X value for new position (coordinates are page-based)
6796      * @param {Number} y Y value for new position (coordinates are page-based)
6797      * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6798      * @return {Ext.Element} this
6799      */
6800     moveTo : function(x, y, animate){
6801         return this.setXY([x, y], this.animTest(arguments, animate, 2));        
6802     },    
6803     
6804     /**
6805      * Gets the left X coordinate
6806      * @param {Boolean} local True to get the local css position instead of page coordinate
6807      * @return {Number}
6808      */
6809     getLeft : function(local){
6810             return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6811     },
6812
6813     /**
6814      * Gets the right X coordinate of the element (element X position + element width)
6815      * @param {Boolean} local True to get the local css position instead of page coordinate
6816      * @return {Number}
6817      */
6818     getRight : function(local){
6819             var me = this;
6820             return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6821     },
6822
6823     /**
6824      * Gets the top Y coordinate
6825      * @param {Boolean} local True to get the local css position instead of page coordinate
6826      * @return {Number}
6827      */
6828     getTop : function(local) {
6829             return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6830     },
6831
6832     /**
6833      * Gets the bottom Y coordinate of the element (element Y position + element height)
6834      * @param {Boolean} local True to get the local css position instead of page coordinate
6835      * @return {Number}
6836      */
6837     getBottom : function(local){
6838             var me = this;
6839             return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6840     },
6841
6842     /**
6843     * Initializes positioning on this element. If a desired position is not passed, it will make the
6844     * the element positioned relative IF it is not already positioned.
6845     * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6846     * @param {Number} zIndex (optional) The zIndex to apply
6847     * @param {Number} x (optional) Set the page X position
6848     * @param {Number} y (optional) Set the page Y position
6849     */
6850     position : function(pos, zIndex, x, y){
6851             var me = this;
6852             
6853         if(!pos && me.isStyle(POSITION, STATIC)){           
6854             me.setStyle(POSITION, RELATIVE);           
6855         } else if(pos) {
6856             me.setStyle(POSITION, pos);
6857         }
6858         if(zIndex){
6859             me.setStyle(ZINDEX, zIndex);
6860         }
6861         if(x || y) me.setXY([x || false, y || false]);
6862     },
6863
6864     /**
6865     * Clear positioning back to the default when the document was loaded
6866     * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6867     * @return {Ext.Element} this
6868      */
6869     clearPositioning : function(value){
6870         value = value || '';
6871         this.setStyle({
6872             left : value,
6873             right : value,
6874             top : value,
6875             bottom : value,
6876             "z-index" : "",
6877             position : STATIC
6878         });
6879         return this;
6880     },
6881
6882     /**
6883     * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6884     * snapshot before performing an update and then restoring the element.
6885     * @return {Object}
6886     */
6887     getPositioning : function(){
6888         var l = this.getStyle(LEFT);
6889         var t = this.getStyle(TOP);
6890         return {
6891             "position" : this.getStyle(POSITION),
6892             "left" : l,
6893             "right" : l ? "" : this.getStyle(RIGHT),
6894             "top" : t,
6895             "bottom" : t ? "" : this.getStyle(BOTTOM),
6896             "z-index" : this.getStyle(ZINDEX)
6897         };
6898     },
6899     
6900     /**
6901     * Set positioning with an object returned by getPositioning().
6902     * @param {Object} posCfg
6903     * @return {Ext.Element} this
6904      */
6905     setPositioning : function(pc){
6906             var me = this,
6907                 style = me.dom.style;
6908                 
6909         me.setStyle(pc);
6910         
6911         if(pc.right == AUTO){
6912             style.right = "";
6913         }
6914         if(pc.bottom == AUTO){
6915             style.bottom = "";
6916         }
6917         
6918         return me;
6919     },    
6920         
6921     /**
6922      * Translates the passed page coordinates into left/top css values for this element
6923      * @param {Number/Array} x The page x or an array containing [x, y]
6924      * @param {Number} y (optional) The page y, required if x is not an array
6925      * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6926      */
6927     translatePoints : function(x, y){                
6928             y = isNaN(x[1]) ? y : x[1];
6929         x = isNaN(x[0]) ? x : x[0];
6930         var me = this,
6931                 relative = me.isStyle(POSITION, RELATIVE),
6932                 o = me.getXY(),
6933                 l = parseInt(me.getStyle(LEFT), 10),
6934                 t = parseInt(me.getStyle(TOP), 10);
6935         
6936         l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6937         t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);        
6938
6939         return {left: (x - o[0] + l), top: (y - o[1] + t)}; 
6940     },
6941     
6942     animTest : function(args, animate, i) {
6943         return !!animate && this.preanim ? this.preanim(args, i) : false;
6944     }
6945 });
6946 })();/**
6947  * @class Ext.Element
6948  */
6949 Ext.Element.addMethods({
6950     /**
6951      * 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.
6952      * @param {Object} box The box to fill {x, y, width, height}
6953      * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6954      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6955      * @return {Ext.Element} this
6956      */
6957     setBox : function(box, adjust, animate){
6958         var me = this,
6959                 w = box.width, 
6960                 h = box.height;
6961         if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6962            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6963            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6964         }
6965         me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6966         return me;
6967     },
6968
6969     /**
6970      * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6971      * set another Element's size/location to match this element.
6972      * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6973      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6974      * @return {Object} box An object in the format<pre><code>
6975 {
6976     x: &lt;Element's X position>,
6977     y: &lt;Element's Y position>,
6978     width: &lt;Element's width>,
6979     height: &lt;Element's height>,
6980     bottom: &lt;Element's lower bound>,
6981     right: &lt;Element's rightmost bound>
6982 }
6983 </code></pre>
6984      * The returned object may also be addressed as an Array where index 0 contains the X position
6985      * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6986      */
6987         getBox : function(contentBox, local) {      
6988             var me = this,
6989                 xy,
6990                 left,
6991                 top,
6992                 getBorderWidth = me.getBorderWidth,
6993                 getPadding = me.getPadding, 
6994                 l,
6995                 r,
6996                 t,
6997                 b;
6998         if(!local){
6999             xy = me.getXY();
7000         }else{
7001             left = parseInt(me.getStyle("left"), 10) || 0;
7002             top = parseInt(me.getStyle("top"), 10) || 0;
7003             xy = [left, top];
7004         }
7005         var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
7006         if(!contentBox){
7007             bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
7008         }else{
7009             l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
7010             r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
7011             t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
7012             b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
7013             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)};
7014         }
7015         bx.right = bx.x + bx.width;
7016         bx.bottom = bx.y + bx.height;
7017         return bx;
7018         },
7019         
7020     /**
7021      * Move this element relative to its current position.
7022      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7023      * @param {Number} distance How far to move the element in pixels
7024      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7025      * @return {Ext.Element} this
7026      */
7027      move : function(direction, distance, animate){
7028         var me = this,          
7029                 xy = me.getXY(),
7030                 x = xy[0],
7031                 y = xy[1],              
7032                 left = [x - distance, y],
7033                 right = [x + distance, y],
7034                 top = [x, y - distance],
7035                 bottom = [x, y + distance],
7036                 hash = {
7037                         l :     left,
7038                         left : left,
7039                         r : right,
7040                         right : right,
7041                         t : top,
7042                         top : top,
7043                         up : top,
7044                         b : bottom, 
7045                         bottom : bottom,
7046                         down : bottom                           
7047                 };
7048         
7049             direction = direction.toLowerCase();    
7050             me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
7051     },
7052     
7053     /**
7054      * Quick set left and top adding default units
7055      * @param {String} left The left CSS property value
7056      * @param {String} top The top CSS property value
7057      * @return {Ext.Element} this
7058      */
7059      setLeftTop : function(left, top){
7060             var me = this,
7061                 style = me.dom.style;
7062         style.left = me.addUnits(left);
7063         style.top = me.addUnits(top);
7064         return me;
7065     },
7066     
7067     /**
7068      * Returns the region of the given element.
7069      * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7070      * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7071      */
7072     getRegion : function(){
7073         return Ext.lib.Dom.getRegion(this.dom);
7074     },
7075     
7076     /**
7077      * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7078      * @param {Number} x X value for new position (coordinates are page-based)
7079      * @param {Number} y Y value for new position (coordinates are page-based)
7080      * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7081      * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7082      * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7083      * </ul></div>
7084      * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7085      * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7086      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7087      * </ul></div>
7088      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7089      * @return {Ext.Element} this
7090      */
7091     setBounds : function(x, y, width, height, animate){
7092             var me = this;
7093         if (!animate || !me.anim) {
7094             me.setSize(width, height);
7095             me.setLocation(x, y);
7096         } else {
7097             me.anim({points: {to: [x, y]}, 
7098                          width: {to: me.adjustWidth(width)}, 
7099                          height: {to: me.adjustHeight(height)}},
7100                      me.preanim(arguments, 4), 
7101                      'motion');
7102         }
7103         return me;
7104     },
7105
7106     /**
7107      * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7108      * @param {Ext.lib.Region} region The region to fill
7109      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7110      * @return {Ext.Element} this
7111      */
7112     setRegion : function(region, animate) {
7113         return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7114     }
7115 });/**
7116  * @class Ext.Element
7117  */
7118 Ext.Element.addMethods({
7119     /**
7120      * Returns true if this element is scrollable.
7121      * @return {Boolean}
7122      */
7123     isScrollable : function(){
7124         var dom = this.dom;
7125         return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7126     },
7127
7128     /**
7129      * 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().
7130      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7131      * @param {Number} value The new scroll value.
7132      * @return {Element} this
7133      */
7134     scrollTo : function(side, value){
7135         this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7136         return this;
7137     },
7138
7139     /**
7140      * Returns the current scroll position of the element.
7141      * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7142      */
7143     getScroll : function(){
7144         var d = this.dom, 
7145             doc = document,
7146             body = doc.body,
7147             docElement = doc.documentElement,
7148             l,
7149             t,
7150             ret;
7151
7152         if(d == doc || d == body){
7153             if(Ext.isIE && Ext.isStrict){
7154                 l = docElement.scrollLeft; 
7155                 t = docElement.scrollTop;
7156             }else{
7157                 l = window.pageXOffset;
7158                 t = window.pageYOffset;
7159             }
7160             ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7161         }else{
7162             ret = {left: d.scrollLeft, top: d.scrollTop};
7163         }
7164         return ret;
7165     }
7166 });/**
7167  * @class Ext.Element
7168  */
7169 Ext.Element.addMethods({
7170     /**
7171      * 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().
7172      * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7173      * @param {Number} value The new scroll value
7174      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7175      * @return {Element} this
7176      */
7177     scrollTo : function(side, value, animate){
7178         var top = /top/i.test(side), //check if we're scrolling top or left
7179                 me = this,
7180                 dom = me.dom,
7181             prop;
7182         if (!animate || !me.anim) {
7183             prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction
7184             dom[prop] = value;
7185         }else{
7186             prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7187             me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},
7188                          me.preanim(arguments, 2), 'scroll');
7189         }
7190         return me;
7191     },
7192     
7193     /**
7194      * Scrolls this element into view within the passed container.
7195      * @param {Mixed} container (optional) The container element to scroll (defaults to document.body).  Should be a
7196      * string (id), dom node, or Ext.Element.
7197      * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7198      * @return {Ext.Element} this
7199      */
7200     scrollIntoView : function(container, hscroll){
7201         var c = Ext.getDom(container) || Ext.getBody().dom,
7202                 el = this.dom,
7203                 o = this.getOffsetsTo(c),
7204             l = o[0] + c.scrollLeft,
7205             t = o[1] + c.scrollTop,
7206             b = t + el.offsetHeight,
7207             r = l + el.offsetWidth,
7208                 ch = c.clientHeight,
7209                 ct = parseInt(c.scrollTop, 10),
7210                 cl = parseInt(c.scrollLeft, 10),
7211                 cb = ct + ch,
7212                 cr = cl + c.clientWidth;
7213
7214         if (el.offsetHeight > ch || t < ct) {
7215                 c.scrollTop = t;
7216         } else if (b > cb){
7217             c.scrollTop = b-ch;
7218         }
7219         c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
7220
7221         if(hscroll !== false){
7222                         if(el.offsetWidth > c.clientWidth || l < cl){
7223                 c.scrollLeft = l;
7224             }else if(r > cr){
7225                 c.scrollLeft = r - c.clientWidth;
7226             }
7227             c.scrollLeft = c.scrollLeft;
7228         }
7229         return this;
7230     },
7231
7232     // private
7233     scrollChildIntoView : function(child, hscroll){
7234         Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7235     },
7236     
7237     /**
7238      * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7239      * within this element's scrollable range.
7240      * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7241      * @param {Number} distance How far to scroll the element in pixels
7242      * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7243      * @return {Boolean} Returns true if a scroll was triggered or false if the element
7244      * was scrolled as far as it could go.
7245      */
7246      scroll : function(direction, distance, animate){
7247          if(!this.isScrollable()){
7248              return;
7249          }
7250          var el = this.dom,
7251             l = el.scrollLeft, t = el.scrollTop,
7252             w = el.scrollWidth, h = el.scrollHeight,
7253             cw = el.clientWidth, ch = el.clientHeight,
7254             scrolled = false, v,
7255             hash = {
7256                 l: Math.min(l + distance, w-cw),
7257                 r: v = Math.max(l - distance, 0),
7258                 t: Math.max(t - distance, 0),
7259                 b: Math.min(t + distance, h-ch)
7260             };
7261             hash.d = hash.b;
7262             hash.u = hash.t;
7263             
7264          direction = direction.substr(0, 1);
7265          if((v = hash[direction]) > -1){
7266             scrolled = true;
7267             this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7268          }
7269          return scrolled;
7270     }
7271 });/**
7272  * @class Ext.Element
7273  */
7274 /**
7275  * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7276  * @static
7277  * @type Number
7278  */
7279 Ext.Element.VISIBILITY = 1;
7280 /**
7281  * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7282  * @static
7283  * @type Number
7284  */
7285 Ext.Element.DISPLAY = 2;
7286
7287 Ext.Element.addMethods(function(){
7288     var VISIBILITY = "visibility",
7289         DISPLAY = "display",
7290         HIDDEN = "hidden",
7291         OFFSETS = "offsets",
7292         NONE = "none",
7293         ORIGINALDISPLAY = 'originalDisplay',
7294         VISMODE = 'visibilityMode',
7295         ELDISPLAY = Ext.Element.DISPLAY,
7296         data = Ext.Element.data,
7297         getDisplay = function(dom){
7298             var d = data(dom, ORIGINALDISPLAY);
7299             if(d === undefined){
7300                 data(dom, ORIGINALDISPLAY, d = '');
7301             }
7302             return d;
7303         },
7304         getVisMode = function(dom){
7305             var m = data(dom, VISMODE);
7306             if(m === undefined){
7307                 data(dom, VISMODE, m = 1);
7308             }
7309             return m;
7310         };
7311
7312     return {
7313         /**
7314          * The element's default display mode  (defaults to "")
7315          * @type String
7316          */
7317         originalDisplay : "",
7318         visibilityMode : 1,
7319
7320         /**
7321          * Sets the element's visibility mode. When setVisible() is called it
7322          * will use this to determine whether to set the visibility or the display property.
7323          * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7324          * @return {Ext.Element} this
7325          */
7326         setVisibilityMode : function(visMode){
7327             data(this.dom, VISMODE, visMode);
7328             return this;
7329         },
7330
7331         /**
7332          * Perform custom animation on this element.
7333          * <div><ul class="mdetail-params">
7334          * <li><u>Animation Properties</u></li>
7335          *
7336          * <p>The Animation Control Object enables gradual transitions for any member of an
7337          * element's style object that takes a numeric value including but not limited to
7338          * these properties:</p><div><ul class="mdetail-params">
7339          * <li><tt>bottom, top, left, right</tt></li>
7340          * <li><tt>height, width</tt></li>
7341          * <li><tt>margin, padding</tt></li>
7342          * <li><tt>borderWidth</tt></li>
7343          * <li><tt>opacity</tt></li>
7344          * <li><tt>fontSize</tt></li>
7345          * <li><tt>lineHeight</tt></li>
7346          * </ul></div>
7347          *
7348          *
7349          * <li><u>Animation Property Attributes</u></li>
7350          *
7351          * <p>Each Animation Property is a config object with optional properties:</p>
7352          * <div><ul class="mdetail-params">
7353          * <li><tt>by</tt>*  : relative change - start at current value, change by this value</li>
7354          * <li><tt>from</tt> : ignore current value, start from this value</li>
7355          * <li><tt>to</tt>*  : start at current value, go to this value</li>
7356          * <li><tt>unit</tt> : any allowable unit specification</li>
7357          * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7358          * </ul></div>
7359          *
7360          * <li><u>Animation Types</u></li>
7361          *
7362          * <p>The supported animation types:</p><div><ul class="mdetail-params">
7363          * <li><tt>'run'</tt> : Default
7364          * <pre><code>
7365 var el = Ext.get('complexEl');
7366 el.animate(
7367     // animation control object
7368     {
7369         borderWidth: {to: 3, from: 0},
7370         opacity: {to: .3, from: 1},
7371         height: {to: 50, from: el.getHeight()},
7372         width: {to: 300, from: el.getWidth()},
7373         top  : {by: - 100, unit: 'px'},
7374     },
7375     0.35,      // animation duration
7376     null,      // callback
7377     'easeOut', // easing method
7378     'run'      // animation type ('run','color','motion','scroll')
7379 );
7380          * </code></pre>
7381          * </li>
7382          * <li><tt>'color'</tt>
7383          * <p>Animates transition of background, text, or border colors.</p>
7384          * <pre><code>
7385 el.animate(
7386     // animation control object
7387     {
7388         color: { to: '#06e' },
7389         backgroundColor: { to: '#e06' }
7390     },
7391     0.35,      // animation duration
7392     null,      // callback
7393     'easeOut', // easing method
7394     'color'    // animation type ('run','color','motion','scroll')
7395 );
7396          * </code></pre>
7397          * </li>
7398          *
7399          * <li><tt>'motion'</tt>
7400          * <p>Animates the motion of an element to/from specific points using optional bezier
7401          * way points during transit.</p>
7402          * <pre><code>
7403 el.animate(
7404     // animation control object
7405     {
7406         borderWidth: {to: 3, from: 0},
7407         opacity: {to: .3, from: 1},
7408         height: {to: 50, from: el.getHeight()},
7409         width: {to: 300, from: el.getWidth()},
7410         top  : {by: - 100, unit: 'px'},
7411         points: {
7412             to: [50, 100],  // go to this point
7413             control: [      // optional bezier way points
7414                 [ 600, 800],
7415                 [-100, 200]
7416             ]
7417         }
7418     },
7419     3000,      // animation duration (milliseconds!)
7420     null,      // callback
7421     'easeOut', // easing method
7422     'motion'   // animation type ('run','color','motion','scroll')
7423 );
7424          * </code></pre>
7425          * </li>
7426          * <li><tt>'scroll'</tt>
7427          * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7428          * <pre><code>
7429 el.animate(
7430     // animation control object
7431     {
7432         scroll: {to: [400, 300]}
7433     },
7434     0.35,      // animation duration
7435     null,      // callback
7436     'easeOut', // easing method
7437     'scroll'   // animation type ('run','color','motion','scroll')
7438 );
7439          * </code></pre>
7440          * </li>
7441          * </ul></div>
7442          *
7443          * </ul></div>
7444          *
7445          * @param {Object} args The animation control args
7446          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7447          * @param {Function} onComplete (optional) Function to call when animation completes
7448          * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7449          * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7450          * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7451          * @return {Ext.Element} this
7452          */
7453         animate : function(args, duration, onComplete, easing, animType){
7454             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7455             return this;
7456         },
7457
7458         /*
7459          * @private Internal animation call
7460          */
7461         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7462             animType = animType || 'run';
7463             opt = opt || {};
7464             var me = this,
7465                 anim = Ext.lib.Anim[animType](
7466                     me.dom,
7467                     args,
7468                     (opt.duration || defaultDur) || .35,
7469                     (opt.easing || defaultEase) || 'easeOut',
7470                     function(){
7471                         if(cb) cb.call(me);
7472                         if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7473                     },
7474                     me
7475                 );
7476             opt.anim = anim;
7477             return anim;
7478         },
7479
7480         // private legacy anim prep
7481         preanim : function(a, i){
7482             return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7483         },
7484
7485         /**
7486          * Checks whether the element is currently visible using both visibility and display properties.
7487          * @return {Boolean} True if the element is currently visible, else false
7488          */
7489         isVisible : function() {
7490             return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7491         },
7492
7493         /**
7494          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7495          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7496          * @param {Boolean} visible Whether the element is visible
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Ext.Element} this
7499          */
7500          setVisible : function(visible, animate){
7501             var me = this, isDisplay, isVisible, isOffsets,
7502                 dom = me.dom;
7503
7504             // hideMode string override
7505             if (typeof animate == 'string'){
7506                 isDisplay = animate == DISPLAY;
7507                 isVisible = animate == VISIBILITY;
7508                 isOffsets = animate == OFFSETS;
7509                 animate = false;
7510             } else {
7511                 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7512                 isVisible = !isDisplay;
7513             }
7514
7515             if (!animate || !me.anim) {
7516                 if (isDisplay){
7517                     me.setDisplayed(visible);
7518                 } else if (isOffsets){
7519                     if (!visible){
7520                         me.hideModeStyles = {
7521                             position: me.getStyle('position'),
7522                             top: me.getStyle('top'),
7523                             left: me.getStyle('left')
7524                         };
7525
7526                         me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7527                     } else {
7528                         me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7529                     }
7530                 }else{
7531                     me.fixDisplay();
7532                     dom.style.visibility = visible ? "visible" : HIDDEN;
7533                 }
7534             }else{
7535                 // closure for composites
7536                 if (visible){
7537                     me.setOpacity(.01);
7538                     me.setVisible(true);
7539                 }
7540                 me.anim({opacity: { to: (visible?1:0) }},
7541                         me.preanim(arguments, 1),
7542                         null,
7543                         .35,
7544                         'easeIn',
7545                         function(){
7546                              if(!visible){
7547                                  dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7548                                  Ext.fly(dom).setOpacity(1);
7549                              }
7550                         });
7551             }
7552             return me;
7553         },
7554
7555         /**
7556          * Toggles the element's visibility or display, depending on visibility mode.
7557          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7558          * @return {Ext.Element} this
7559          */
7560         toggle : function(animate){
7561             var me = this;
7562             me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7563             return me;
7564         },
7565
7566         /**
7567          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7568          * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7569          * @return {Ext.Element} this
7570          */
7571         setDisplayed : function(value) {
7572             if(typeof value == "boolean"){
7573                value = value ? getDisplay(this.dom) : NONE;
7574             }
7575             this.setStyle(DISPLAY, value);
7576             return this;
7577         },
7578
7579         // private
7580         fixDisplay : function(){
7581             var me = this;
7582             if(me.isStyle(DISPLAY, NONE)){
7583                 me.setStyle(VISIBILITY, HIDDEN);
7584                 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7585                 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7586                     me.setStyle(DISPLAY, "block");
7587                 }
7588             }
7589         },
7590
7591         /**
7592          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7593          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7594          * @return {Ext.Element} this
7595          */
7596         hide : function(animate){
7597             // hideMode override
7598             if (typeof animate == 'string'){
7599                 this.setVisible(false, animate);
7600                 return this;
7601             }
7602             this.setVisible(false, this.preanim(arguments, 0));
7603             return this;
7604         },
7605
7606         /**
7607         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7608         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7609          * @return {Ext.Element} this
7610          */
7611         show : function(animate){
7612             // hideMode override
7613             if (typeof animate == 'string'){
7614                 this.setVisible(true, animate);
7615                 return this;
7616             }
7617             this.setVisible(true, this.preanim(arguments, 0));
7618             return this;
7619         }
7620     };
7621 }());
7622 /**
7623  * @class Ext.Element
7624  */
7625 Ext.Element.addMethods(
7626 function(){
7627     var VISIBILITY = "visibility",
7628         DISPLAY = "display",
7629         HIDDEN = "hidden",
7630         NONE = "none",
7631             XMASKED = "x-masked",
7632                 XMASKEDRELATIVE = "x-masked-relative",
7633         data = Ext.Element.data;
7634
7635         return {
7636                 /**
7637              * Checks whether the element is currently visible using both visibility and display properties.
7638              * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7639              * @return {Boolean} True if the element is currently visible, else false
7640              */
7641             isVisible : function(deep) {
7642                 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7643                         p = this.dom.parentNode;
7644                 if(deep !== true || !vis){
7645                     return vis;
7646                 }
7647                 while(p && !/^body/i.test(p.tagName)){
7648                     if(!Ext.fly(p, '_isVisible').isVisible()){
7649                         return false;
7650                     }
7651                     p = p.parentNode;
7652                 }
7653                 return true;
7654             },
7655
7656             /**
7657              * Returns true if display is not "none"
7658              * @return {Boolean}
7659              */
7660             isDisplayed : function() {
7661                 return !this.isStyle(DISPLAY, NONE);
7662             },
7663
7664                 /**
7665              * Convenience method for setVisibilityMode(Element.DISPLAY)
7666              * @param {String} display (optional) What to set display to when visible
7667              * @return {Ext.Element} this
7668              */
7669             enableDisplayMode : function(display){
7670                 this.setVisibilityMode(Ext.Element.DISPLAY);
7671                 if(!Ext.isEmpty(display)){
7672                 data(this.dom, 'originalDisplay', display);
7673             }
7674                 return this;
7675             },
7676
7677                 /**
7678              * Puts a mask over this element to disable user interaction. Requires core.css.
7679              * This method can only be applied to elements which accept child nodes.
7680              * @param {String} msg (optional) A message to display in the mask
7681              * @param {String} msgCls (optional) A css class to apply to the msg element
7682              * @return {Element} The mask element
7683              */
7684             mask : function(msg, msgCls){
7685                     var me = this,
7686                         dom = me.dom,
7687                         dh = Ext.DomHelper,
7688                         EXTELMASKMSG = "ext-el-mask-msg",
7689                 el,
7690                 mask;
7691
7692                 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7693                     me.addClass(XMASKEDRELATIVE);
7694                 }
7695                 if((el = data(dom, 'maskMsg'))){
7696                     el.remove();
7697                 }
7698                 if((el = data(dom, 'mask'))){
7699                     el.remove();
7700                 }
7701
7702             mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7703                 data(dom, 'mask', mask);
7704
7705                 me.addClass(XMASKED);
7706                 mask.setDisplayed(true);
7707                 if(typeof msg == 'string'){
7708                 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7709                 data(dom, 'maskMsg', mm);
7710                     mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7711                     mm.dom.firstChild.innerHTML = msg;
7712                     mm.setDisplayed(true);
7713                     mm.center(me);
7714                 }
7715                 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7716                     mask.setSize(undefined, me.getHeight());
7717                 }
7718                 return mask;
7719             },
7720
7721             /**
7722              * Removes a previously applied mask.
7723              */
7724             unmask : function(){
7725                     var me = this,
7726                 dom = me.dom,
7727                         mask = data(dom, 'mask'),
7728                         maskMsg = data(dom, 'maskMsg');
7729                 if(mask){
7730                     if(maskMsg){
7731                         maskMsg.remove();
7732                     data(dom, 'maskMsg', undefined);
7733                     }
7734                     mask.remove();
7735                 data(dom, 'mask', undefined);
7736                 }
7737                 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7738             },
7739
7740             /**
7741              * Returns true if this element is masked
7742              * @return {Boolean}
7743              */
7744             isMasked : function(){
7745             var m = data(this.dom, 'mask');
7746                 return m && m.isVisible();
7747             },
7748
7749             /**
7750              * Creates an iframe shim for this element to keep selects and other windowed objects from
7751              * showing through.
7752              * @return {Ext.Element} The new shim element
7753              */
7754             createShim : function(){
7755                 var el = document.createElement('iframe'),
7756                         shim;
7757                 el.frameBorder = '0';
7758                 el.className = 'ext-shim';
7759                 el.src = Ext.SSL_SECURE_URL;
7760                 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7761                 shim.autoBoxAdjust = false;
7762                 return shim;
7763             }
7764     };
7765 }());/**
7766  * @class Ext.Element
7767  */
7768 Ext.Element.addMethods({
7769     /**
7770      * Convenience method for constructing a KeyMap
7771      * @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:
7772      * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7773      * @param {Function} fn The function to call
7774      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7775      * @return {Ext.KeyMap} The KeyMap created
7776      */
7777     addKeyListener : function(key, fn, scope){
7778         var config;
7779         if(typeof key != 'object' || Ext.isArray(key)){
7780             config = {
7781                 key: key,
7782                 fn: fn,
7783                 scope: scope
7784             };
7785         }else{
7786             config = {
7787                 key : key.key,
7788                 shift : key.shift,
7789                 ctrl : key.ctrl,
7790                 alt : key.alt,
7791                 fn: fn,
7792                 scope: scope
7793             };
7794         }
7795         return new Ext.KeyMap(this, config);
7796     },
7797
7798     /**
7799      * Creates a KeyMap for this element
7800      * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7801      * @return {Ext.KeyMap} The KeyMap created
7802      */
7803     addKeyMap : function(config){
7804         return new Ext.KeyMap(this, config);
7805     }
7806 });
7807 (function(){
7808     // contants
7809     var NULL = null,
7810         UNDEFINED = undefined,
7811         TRUE = true,
7812         FALSE = false,
7813         SETX = "setX",
7814         SETY = "setY",
7815         SETXY = "setXY",
7816         LEFT = "left",
7817         BOTTOM = "bottom",
7818         TOP = "top",
7819         RIGHT = "right",
7820         HEIGHT = "height",
7821         WIDTH = "width",
7822         POINTS = "points",
7823         HIDDEN = "hidden",
7824         ABSOLUTE = "absolute",
7825         VISIBLE = "visible",
7826         MOTION = "motion",
7827         POSITION = "position",
7828         EASEOUT = "easeOut",
7829         /*
7830          * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7831          */
7832         flyEl = new Ext.Element.Flyweight(),
7833         queues = {},
7834         getObject = function(o){
7835             return o || {};
7836         },
7837         fly = function(dom){
7838             flyEl.dom = dom;
7839             flyEl.id = Ext.id(dom);
7840             return flyEl;
7841         },
7842         /*
7843          * Queueing now stored outside of the element due to closure issues
7844          */
7845         getQueue = function(id){
7846             if(!queues[id]){
7847                 queues[id] = [];
7848             }
7849             return queues[id];
7850         },
7851         setQueue = function(id, value){
7852             queues[id] = value;
7853         };
7854         
7855 //Notifies Element that fx methods are available
7856 Ext.enableFx = TRUE;
7857
7858 /**
7859  * @class Ext.Fx
7860  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
7861  * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7862  * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7863  * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7864  * 
7865  * <p><b><u>Method Chaining</u></b></p>
7866  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7867  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7868  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7869  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
7870  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7871  * expected results and should be done with care.  Also see <tt>{@link #callback}</tt>.</p><br/>
7872  *
7873  * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7874  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7875  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
7876 <pre>
7877 Value  Description
7878 -----  -----------------------------
7879 tl     The top left corner
7880 t      The center of the top edge
7881 tr     The top right corner
7882 l      The center of the left edge
7883 r      The center of the right edge
7884 bl     The bottom left corner
7885 b      The center of the bottom edge
7886 br     The bottom right corner
7887 </pre>
7888  * <b>Note</b>: some Fx methods accept specific custom config parameters.  The options shown in the Config Options
7889  * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7890  * 
7891  * @cfg {Function} callback A function called when the effect is finished.  Note that effects are queued internally by the
7892  * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7893  * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7894  * el.slideIn().highlight();
7895  * </code></pre>
7896  * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7897  * being operated upon is passed as the first parameter.
7898  * 
7899  * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7900  * 
7901  * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7902  * <li><b><tt>backBoth</tt></b></li>
7903  * <li><b><tt>backIn</tt></b></li>
7904  * <li><b><tt>backOut</tt></b></li>
7905  * <li><b><tt>bounceBoth</tt></b></li>
7906  * <li><b><tt>bounceIn</tt></b></li>
7907  * <li><b><tt>bounceOut</tt></b></li>
7908  * <li><b><tt>easeBoth</tt></b></li>
7909  * <li><b><tt>easeBothStrong</tt></b></li>
7910  * <li><b><tt>easeIn</tt></b></li>
7911  * <li><b><tt>easeInStrong</tt></b></li>
7912  * <li><b><tt>easeNone</tt></b></li>
7913  * <li><b><tt>easeOut</tt></b></li>
7914  * <li><b><tt>easeOutStrong</tt></b></li>
7915  * <li><b><tt>elasticBoth</tt></b></li>
7916  * <li><b><tt>elasticIn</tt></b></li>
7917  * <li><b><tt>elasticOut</tt></b></li>
7918  * </ul></div>
7919  *
7920  * @cfg {String} afterCls A css class to apply after the effect
7921  * @cfg {Number} duration The length of time (in seconds) that the effect should last
7922  * 
7923  * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7924  * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7925  *  
7926  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7927  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
7928  * effects that end with the element being visually hidden, ignored otherwise)
7929  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7930  * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7931  * Element after the effect finishes.
7932  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7933  * @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
7934  * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7935  */
7936 Ext.Fx = {
7937     
7938     // private - calls the function taking arguments from the argHash based on the key.  Returns the return value of the function.
7939     //           this is useful for replacing switch statements (for example).
7940     switchStatements : function(key, fn, argHash){
7941         return fn.apply(this, argHash[key]);
7942     },
7943     
7944     /**
7945      * Slides the element into view.  An anchor point can be optionally passed to set the point of
7946      * origin for the slide effect.  This function automatically handles wrapping the element with
7947      * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
7948      * Usage:
7949      *<pre><code>
7950 // default: slide the element in from the top
7951 el.slideIn();
7952
7953 // custom: slide the element in from the right with a 2-second duration
7954 el.slideIn('r', { duration: 2 });
7955
7956 // common config options shown with default values
7957 el.slideIn('t', {
7958     easing: 'easeOut',
7959     duration: .5
7960 });
7961 </code></pre>
7962      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7963      * @param {Object} options (optional) Object literal with any of the Fx config options
7964      * @return {Ext.Element} The Element
7965      */
7966     slideIn : function(anchor, o){ 
7967         o = getObject(o);
7968         var me = this,
7969             dom = me.dom,
7970             st = dom.style,
7971             xy,
7972             r,
7973             b,              
7974             wrap,               
7975             after,
7976             st,
7977             args, 
7978             pt,
7979             bw,
7980             bh;
7981             
7982         anchor = anchor || "t";
7983
7984         me.queueFx(o, function(){            
7985             xy = fly(dom).getXY();
7986             // fix display to visibility
7987             fly(dom).fixDisplay();            
7988             
7989             // restore values after effect
7990             r = fly(dom).getFxRestore();      
7991             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
7992             b.right = b.x + b.width;
7993             b.bottom = b.y + b.height;
7994             
7995             // fixed size for slide
7996             fly(dom).setWidth(b.width).setHeight(b.height);            
7997             
7998             // wrap if needed
7999             wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
8000             
8001             st.visibility = VISIBLE;
8002             st.position = ABSOLUTE;
8003             
8004             // clear out temp styles after slide and unwrap
8005             function after(){
8006                  fly(dom).fxUnwrap(wrap, r.pos, o);
8007                  st.width = r.width;
8008                  st.height = r.height;
8009                  fly(dom).afterFx(o);
8010             }
8011             
8012             // time to calculate the positions        
8013             pt = {to: [b.x, b.y]}; 
8014             bw = {to: b.width};
8015             bh = {to: b.height};
8016                 
8017             function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){                    
8018                 var ret = {};
8019                 fly(wrap).setWidth(ww).setHeight(wh);
8020                 if(fly(wrap)[sXY]){
8021                     fly(wrap)[sXY](sXYval);                  
8022                 }
8023                 style[s1] = style[s2] = "0";                    
8024                 if(w){
8025                     ret.width = w
8026                 };
8027                 if(h){
8028                     ret.height = h;
8029                 }
8030                 if(p){
8031                     ret.points = p;
8032                 }
8033                 return ret;
8034             };
8035
8036             args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8037                     t  : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
8038                     l  : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
8039                     r  : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
8040                     b  : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
8041                     tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
8042                     bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
8043                     br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
8044                     tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
8045                 });
8046             
8047             st.visibility = VISIBLE;
8048             fly(wrap).show();
8049
8050             arguments.callee.anim = fly(wrap).fxanim(args,
8051                 o,
8052                 MOTION,
8053                 .5,
8054                 EASEOUT, 
8055                 after);
8056         });
8057         return me;
8058     },
8059     
8060     /**
8061      * Slides the element out of view.  An anchor point can be optionally passed to set the end point
8062      * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
8063      * 'hidden') but block elements will still take up space in the document.  The element must be removed
8064      * from the DOM using the 'remove' config option if desired.  This function automatically handles 
8065      * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
8066      * Usage:
8067      *<pre><code>
8068 // default: slide the element out to the top
8069 el.slideOut();
8070
8071 // custom: slide the element out to the right with a 2-second duration
8072 el.slideOut('r', { duration: 2 });
8073
8074 // common config options shown with default values
8075 el.slideOut('t', {
8076     easing: 'easeOut',
8077     duration: .5,
8078     remove: false,
8079     useDisplay: false
8080 });
8081 </code></pre>
8082      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8083      * @param {Object} options (optional) Object literal with any of the Fx config options
8084      * @return {Ext.Element} The Element
8085      */
8086     slideOut : function(anchor, o){
8087         o = getObject(o);
8088         var me = this,
8089             dom = me.dom,
8090             st = dom.style,
8091             xy = me.getXY(),
8092             wrap,
8093             r,
8094             b,
8095             a,
8096             zero = {to: 0}; 
8097                     
8098         anchor = anchor || "t";
8099
8100         me.queueFx(o, function(){
8101             
8102             // restore values after effect
8103             r = fly(dom).getFxRestore(); 
8104             b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8105             b.right = b.x + b.width;
8106             b.bottom = b.y + b.height;
8107                 
8108             // fixed size for slide   
8109             fly(dom).setWidth(b.width).setHeight(b.height);
8110
8111             // wrap if needed
8112             wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8113                 
8114             st.visibility = VISIBLE;
8115             st.position = ABSOLUTE;
8116             fly(wrap).setWidth(b.width).setHeight(b.height);            
8117
8118             function after(){
8119                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                
8120                 fly(dom).fxUnwrap(wrap, r.pos, o);
8121                 st.width = r.width;
8122                 st.height = r.height;
8123                 fly(dom).afterFx(o);
8124             }            
8125             
8126             function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){                    
8127                 var ret = {};
8128                 
8129                 style[s1] = style[s2] = "0";
8130                 ret[p1] = v1;               
8131                 if(p2){
8132                     ret[p2] = v2;               
8133                 }
8134                 if(p3){
8135                     ret[p3] = v3;
8136                 }
8137                 
8138                 return ret;
8139             };
8140             
8141             a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8142                 t  : [st, LEFT, BOTTOM, HEIGHT, zero],
8143                 l  : [st, RIGHT, TOP, WIDTH, zero],
8144                 r  : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8145                 b  : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8146                 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8147                 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8148                 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8149                 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8150             });
8151             
8152             arguments.callee.anim = fly(wrap).fxanim(a,
8153                 o,
8154                 MOTION,
8155                 .5,
8156                 EASEOUT, 
8157                 after);
8158         });
8159         return me;
8160     },
8161
8162     /**
8163      * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
8164      * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
8165      * The element must be removed from the DOM using the 'remove' config option if desired.
8166      * Usage:
8167      *<pre><code>
8168 // default
8169 el.puff();
8170
8171 // common config options shown with default values
8172 el.puff({
8173     easing: 'easeOut',
8174     duration: .5,
8175     remove: false,
8176     useDisplay: false
8177 });
8178 </code></pre>
8179      * @param {Object} options (optional) Object literal with any of the Fx config options
8180      * @return {Ext.Element} The Element
8181      */
8182     puff : function(o){
8183         o = getObject(o);
8184         var me = this,
8185             dom = me.dom,
8186             st = dom.style,
8187             width,
8188             height,
8189             r;
8190
8191         me.queueFx(o, function(){
8192             width = fly(dom).getWidth();
8193             height = fly(dom).getHeight();
8194             fly(dom).clearOpacity();
8195             fly(dom).show();
8196
8197             // restore values after effect
8198             r = fly(dom).getFxRestore();                   
8199             
8200             function after(){
8201                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();                  
8202                 fly(dom).clearOpacity();  
8203                 fly(dom).setPositioning(r.pos);
8204                 st.width = r.width;
8205                 st.height = r.height;
8206                 st.fontSize = '';
8207                 fly(dom).afterFx(o);
8208             }   
8209
8210             arguments.callee.anim = fly(dom).fxanim({
8211                     width : {to : fly(dom).adjustWidth(width * 2)},
8212                     height : {to : fly(dom).adjustHeight(height * 2)},
8213                     points : {by : [-width * .5, -height * .5]},
8214                     opacity : {to : 0},
8215                     fontSize: {to : 200, unit: "%"}
8216                 },
8217                 o,
8218                 MOTION,
8219                 .5,
8220                 EASEOUT,
8221                  after);
8222         });
8223         return me;
8224     },
8225
8226     /**
8227      * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8228      * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
8229      * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8230      * Usage:
8231      *<pre><code>
8232 // default
8233 el.switchOff();
8234
8235 // all config options shown with default values
8236 el.switchOff({
8237     easing: 'easeIn',
8238     duration: .3,
8239     remove: false,
8240     useDisplay: false
8241 });
8242 </code></pre>
8243      * @param {Object} options (optional) Object literal with any of the Fx config options
8244      * @return {Ext.Element} The Element
8245      */
8246     switchOff : function(o){
8247         o = getObject(o);
8248         var me = this,
8249             dom = me.dom,
8250             st = dom.style,
8251             r;
8252
8253         me.queueFx(o, function(){
8254             fly(dom).clearOpacity();
8255             fly(dom).clip();
8256
8257             // restore values after effect
8258             r = fly(dom).getFxRestore();
8259                 
8260             function after(){
8261                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();  
8262                 fly(dom).clearOpacity();
8263                 fly(dom).setPositioning(r.pos);
8264                 st.width = r.width;
8265                 st.height = r.height;   
8266                 fly(dom).afterFx(o);
8267             };
8268
8269             fly(dom).fxanim({opacity : {to : 0.3}}, 
8270                 NULL, 
8271                 NULL, 
8272                 .1, 
8273                 NULL, 
8274                 function(){                                 
8275                     fly(dom).clearOpacity();
8276                         (function(){                            
8277                             fly(dom).fxanim({
8278                                 height : {to : 1},
8279                                 points : {by : [0, fly(dom).getHeight() * .5]}
8280                             }, 
8281                             o, 
8282                             MOTION, 
8283                             0.3, 
8284                             'easeIn', 
8285                             after);
8286                         }).defer(100);
8287                 });
8288         });
8289         return me;
8290     },
8291
8292     /**
8293      * Highlights the Element by setting a color (applies to the background-color by default, but can be
8294      * changed using the "attr" config option) and then fading back to the original color. If no original
8295      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8296      * Usage:
8297 <pre><code>
8298 // default: highlight background to yellow
8299 el.highlight();
8300
8301 // custom: highlight foreground text to blue for 2 seconds
8302 el.highlight("0000ff", { attr: 'color', duration: 2 });
8303
8304 // common config options shown with default values
8305 el.highlight("ffff9c", {
8306     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8307     endColor: (current color) or "ffffff",
8308     easing: 'easeIn',
8309     duration: 1
8310 });
8311 </code></pre>
8312      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8313      * @param {Object} options (optional) Object literal with any of the Fx config options
8314      * @return {Ext.Element} The Element
8315      */ 
8316     highlight : function(color, o){
8317         o = getObject(o);
8318         var me = this,
8319             dom = me.dom,
8320             attr = o.attr || "backgroundColor",
8321             a = {},
8322             restore;
8323
8324         me.queueFx(o, function(){
8325             fly(dom).clearOpacity();
8326             fly(dom).show();
8327
8328             function after(){
8329                 dom.style[attr] = restore;
8330                 fly(dom).afterFx(o);
8331             }            
8332             restore = dom.style[attr];
8333             a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8334             arguments.callee.anim = fly(dom).fxanim(a,
8335                 o,
8336                 'color',
8337                 1,
8338                 'easeIn', 
8339                 after);
8340         });
8341         return me;
8342     },
8343
8344    /**
8345     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8346     * Usage:
8347 <pre><code>
8348 // default: a single light blue ripple
8349 el.frame();
8350
8351 // custom: 3 red ripples lasting 3 seconds total
8352 el.frame("ff0000", 3, { duration: 3 });
8353
8354 // common config options shown with default values
8355 el.frame("C3DAF9", 1, {
8356     duration: 1 //duration of each individual ripple.
8357     // Note: Easing is not configurable and will be ignored if included
8358 });
8359 </code></pre>
8360     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8361     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8362     * @param {Object} options (optional) Object literal with any of the Fx config options
8363     * @return {Ext.Element} The Element
8364     */
8365     frame : function(color, count, o){
8366         o = getObject(o);
8367         var me = this,
8368             dom = me.dom,
8369             proxy,
8370             active;
8371
8372         me.queueFx(o, function(){
8373             color = color || '#C3DAF9';
8374             if(color.length == 6){
8375                 color = '#' + color;
8376             }            
8377             count = count || 1;
8378             fly(dom).show();
8379
8380             var xy = fly(dom).getXY(),
8381                 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8382                 queue = function(){
8383                     proxy = fly(document.body || document.documentElement).createChild({
8384                         style:{
8385                             position : ABSOLUTE,
8386                             'z-index': 35000, // yee haw
8387                             border : '0px solid ' + color
8388                         }
8389                     });
8390                     return proxy.queueFx({}, animFn);
8391                 };
8392             
8393             
8394             arguments.callee.anim = {
8395                 isAnimated: true,
8396                 stop: function() {
8397                     count = 0;
8398                     proxy.stopFx();
8399                 }
8400             };
8401             
8402             function animFn(){
8403                 var scale = Ext.isBorderBox ? 2 : 1;
8404                 active = proxy.anim({
8405                     top : {from : b.y, to : b.y - 20},
8406                     left : {from : b.x, to : b.x - 20},
8407                     borderWidth : {from : 0, to : 10},
8408                     opacity : {from : 1, to : 0},
8409                     height : {from : b.height, to : b.height + 20 * scale},
8410                     width : {from : b.width, to : b.width + 20 * scale}
8411                 },{
8412                     duration: o.duration || 1,
8413                     callback: function() {
8414                         proxy.remove();
8415                         --count > 0 ? queue() : fly(dom).afterFx(o);
8416                     }
8417                 });
8418                 arguments.callee.anim = {
8419                     isAnimated: true,
8420                     stop: function(){
8421                         active.stop();
8422                     }
8423                 };
8424             };
8425             queue();
8426         });
8427         return me;
8428     },
8429
8430    /**
8431     * Creates a pause before any subsequent queued effects begin.  If there are
8432     * no effects queued after the pause it will have no effect.
8433     * Usage:
8434 <pre><code>
8435 el.pause(1);
8436 </code></pre>
8437     * @param {Number} seconds The length of time to pause (in seconds)
8438     * @return {Ext.Element} The Element
8439     */
8440     pause : function(seconds){        
8441         var dom = this.dom,
8442             t;
8443
8444         this.queueFx({}, function(){
8445             t = setTimeout(function(){
8446                 fly(dom).afterFx({});
8447             }, seconds * 1000);
8448             arguments.callee.anim = {
8449                 isAnimated: true,
8450                 stop: function(){
8451                     clearTimeout(t);
8452                     fly(dom).afterFx({});
8453                 }
8454             };
8455         });
8456         return this;
8457     },
8458
8459    /**
8460     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
8461     * using the <tt>{@link #endOpacity}</tt> config option.
8462     * Usage:
8463 <pre><code>
8464 // default: fade in from opacity 0 to 100%
8465 el.fadeIn();
8466
8467 // custom: fade in from opacity 0 to 75% over 2 seconds
8468 el.fadeIn({ endOpacity: .75, duration: 2});
8469
8470 // common config options shown with default values
8471 el.fadeIn({
8472     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8473     easing: 'easeOut',
8474     duration: .5
8475 });
8476 </code></pre>
8477     * @param {Object} options (optional) Object literal with any of the Fx config options
8478     * @return {Ext.Element} The Element
8479     */
8480     fadeIn : function(o){
8481         o = getObject(o);
8482         var me = this,
8483             dom = me.dom,
8484             to = o.endOpacity || 1;
8485         
8486         me.queueFx(o, function(){
8487             fly(dom).setOpacity(0);
8488             fly(dom).fixDisplay();
8489             dom.style.visibility = VISIBLE;
8490             arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8491                 o, NULL, .5, EASEOUT, function(){
8492                 if(to == 1){
8493                     fly(dom).clearOpacity();
8494                 }
8495                 fly(dom).afterFx(o);
8496             });
8497         });
8498         return me;
8499     },
8500
8501    /**
8502     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
8503     * using the <tt>{@link #endOpacity}</tt> config option.  Note that IE may require
8504     * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8505     * Usage:
8506 <pre><code>
8507 // default: fade out from the element's current opacity to 0
8508 el.fadeOut();
8509
8510 // custom: fade out from the element's current opacity to 25% over 2 seconds
8511 el.fadeOut({ endOpacity: .25, duration: 2});
8512
8513 // common config options shown with default values
8514 el.fadeOut({
8515     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8516     easing: 'easeOut',
8517     duration: .5,
8518     remove: false,
8519     useDisplay: false
8520 });
8521 </code></pre>
8522     * @param {Object} options (optional) Object literal with any of the Fx config options
8523     * @return {Ext.Element} The Element
8524     */
8525     fadeOut : function(o){
8526         o = getObject(o);
8527         var me = this,
8528             dom = me.dom,
8529             style = dom.style,
8530             to = o.endOpacity || 0;         
8531         
8532         me.queueFx(o, function(){  
8533             arguments.callee.anim = fly(dom).fxanim({ 
8534                 opacity : {to : to}},
8535                 o, 
8536                 NULL, 
8537                 .5, 
8538                 EASEOUT, 
8539                 function(){
8540                     if(to == 0){
8541                         Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ? 
8542                             style.display = "none" :
8543                             style.visibility = HIDDEN;
8544                             
8545                         fly(dom).clearOpacity();
8546                     }
8547                     fly(dom).afterFx(o);
8548             });
8549         });
8550         return me;
8551     },
8552
8553    /**
8554     * Animates the transition of an element's dimensions from a starting height/width
8555     * to an ending height/width.  This method is a convenience implementation of {@link shift}.
8556     * Usage:
8557 <pre><code>
8558 // change height and width to 100x100 pixels
8559 el.scale(100, 100);
8560
8561 // common config options shown with default values.  The height and width will default to
8562 // the element&#39;s existing values if passed as null.
8563 el.scale(
8564     [element&#39;s width],
8565     [element&#39;s height], {
8566         easing: 'easeOut',
8567         duration: .35
8568     }
8569 );
8570 </code></pre>
8571     * @param {Number} width  The new width (pass undefined to keep the original width)
8572     * @param {Number} height  The new height (pass undefined to keep the original height)
8573     * @param {Object} options (optional) Object literal with any of the Fx config options
8574     * @return {Ext.Element} The Element
8575     */
8576     scale : function(w, h, o){
8577         this.shift(Ext.apply({}, o, {
8578             width: w,
8579             height: h
8580         }));
8581         return this;
8582     },
8583
8584    /**
8585     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8586     * Any of these properties not specified in the config object will not be changed.  This effect 
8587     * requires that at least one new dimension, position or opacity setting must be passed in on
8588     * the config object in order for the function to have any effect.
8589     * Usage:
8590 <pre><code>
8591 // slide the element horizontally to x position 200 while changing the height and opacity
8592 el.shift({ x: 200, height: 50, opacity: .8 });
8593
8594 // common config options shown with default values.
8595 el.shift({
8596     width: [element&#39;s width],
8597     height: [element&#39;s height],
8598     x: [element&#39;s x position],
8599     y: [element&#39;s y position],
8600     opacity: [element&#39;s opacity],
8601     easing: 'easeOut',
8602     duration: .35
8603 });
8604 </code></pre>
8605     * @param {Object} options  Object literal with any of the Fx config options
8606     * @return {Ext.Element} The Element
8607     */
8608     shift : function(o){
8609         o = getObject(o);
8610         var dom = this.dom,
8611             a = {};
8612                 
8613         this.queueFx(o, function(){
8614             for (var prop in o) {
8615                 if (o[prop] != UNDEFINED) {                                                 
8616                     a[prop] = {to : o[prop]};                   
8617                 }
8618             } 
8619             
8620             a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8621             a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;   
8622             
8623             if (a.x || a.y || a.xy) {
8624                 a.points = a.xy || 
8625                            {to : [ a.x ? a.x.to : fly(dom).getX(),
8626                                    a.y ? a.y.to : fly(dom).getY()]};                  
8627             }
8628
8629             arguments.callee.anim = fly(dom).fxanim(a,
8630                 o, 
8631                 MOTION, 
8632                 .35, 
8633                 EASEOUT, 
8634                 function(){
8635                     fly(dom).afterFx(o);
8636                 });
8637         });
8638         return this;
8639     },
8640
8641     /**
8642      * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
8643      * ending point of the effect.
8644      * Usage:
8645      *<pre><code>
8646 // default: slide the element downward while fading out
8647 el.ghost();
8648
8649 // custom: slide the element out to the right with a 2-second duration
8650 el.ghost('r', { duration: 2 });
8651
8652 // common config options shown with default values
8653 el.ghost('b', {
8654     easing: 'easeOut',
8655     duration: .5,
8656     remove: false,
8657     useDisplay: false
8658 });
8659 </code></pre>
8660      * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8661      * @param {Object} options (optional) Object literal with any of the Fx config options
8662      * @return {Ext.Element} The Element
8663      */
8664     ghost : function(anchor, o){
8665         o = getObject(o);
8666         var me = this,
8667             dom = me.dom,
8668             st = dom.style,
8669             a = {opacity: {to: 0}, points: {}},
8670             pt = a.points,
8671             r,
8672             w,
8673             h;
8674             
8675         anchor = anchor || "b";
8676
8677         me.queueFx(o, function(){
8678             // restore values after effect
8679             r = fly(dom).getFxRestore();
8680             w = fly(dom).getWidth();
8681             h = fly(dom).getHeight();
8682             
8683             function after(){
8684                 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();   
8685                 fly(dom).clearOpacity();
8686                 fly(dom).setPositioning(r.pos);
8687                 st.width = r.width;
8688                 st.height = r.height;
8689                 fly(dom).afterFx(o);
8690             }
8691                 
8692             pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8693                t  : [0, -h],
8694                l  : [-w, 0],
8695                r  : [w, 0],
8696                b  : [0, h],
8697                tl : [-w, -h],
8698                bl : [-w, h],
8699                br : [w, h],
8700                tr : [w, -h] 
8701             });
8702                 
8703             arguments.callee.anim = fly(dom).fxanim(a,
8704                 o,
8705                 MOTION,
8706                 .5,
8707                 EASEOUT, after);
8708         });
8709         return me;
8710     },
8711
8712     /**
8713      * Ensures that all effects queued after syncFx is called on the element are
8714      * run concurrently.  This is the opposite of {@link #sequenceFx}.
8715      * @return {Ext.Element} The Element
8716      */
8717     syncFx : function(){
8718         var me = this;
8719         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8720             block : FALSE,
8721             concurrent : TRUE,
8722             stopFx : FALSE
8723         });
8724         return me;
8725     },
8726
8727     /**
8728      * Ensures that all effects queued after sequenceFx is called on the element are
8729      * run in sequence.  This is the opposite of {@link #syncFx}.
8730      * @return {Ext.Element} The Element
8731      */
8732     sequenceFx : function(){
8733         var me = this;
8734         me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8735             block : FALSE,
8736             concurrent : FALSE,
8737             stopFx : FALSE
8738         });
8739         return me;
8740     },
8741
8742     /* @private */
8743     nextFx : function(){        
8744         var ef = getQueue(this.dom.id)[0];
8745         if(ef){
8746             ef.call(this);
8747         }
8748     },
8749
8750     /**
8751      * Returns true if the element has any effects actively running or queued, else returns false.
8752      * @return {Boolean} True if element has active effects, else false
8753      */
8754     hasActiveFx : function(){
8755         return getQueue(this.dom.id)[0];
8756     },
8757
8758     /**
8759      * Stops any running effects and clears the element's internal effects queue if it contains
8760      * any additional effects that haven't started yet.
8761      * @return {Ext.Element} The Element
8762      */
8763     stopFx : function(finish){
8764         var me = this,
8765             id = me.dom.id;
8766         if(me.hasActiveFx()){
8767             var cur = getQueue(id)[0];
8768             if(cur && cur.anim){
8769                 if(cur.anim.isAnimated){
8770                     setQueue(id, [cur]); //clear
8771                     cur.anim.stop(finish !== undefined ? finish : TRUE);
8772                 }else{
8773                     setQueue(id, []);
8774                 }
8775             }
8776         }
8777         return me;
8778     },
8779
8780     /* @private */
8781     beforeFx : function(o){
8782         if(this.hasActiveFx() && !o.concurrent){
8783            if(o.stopFx){
8784                this.stopFx();
8785                return TRUE;
8786            }
8787            return FALSE;
8788         }
8789         return TRUE;
8790     },
8791
8792     /**
8793      * Returns true if the element is currently blocking so that no other effect can be queued
8794      * until this effect is finished, else returns false if blocking is not set.  This is commonly
8795      * used to ensure that an effect initiated by a user action runs to completion prior to the
8796      * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8797      * @return {Boolean} True if blocking, else false
8798      */
8799     hasFxBlock : function(){
8800         var q = getQueue(this.dom.id);
8801         return q && q[0] && q[0].block;
8802     },
8803
8804     /* @private */
8805     queueFx : function(o, fn){
8806         var me = fly(this.dom);
8807         if(!me.hasFxBlock()){
8808             Ext.applyIf(o, me.fxDefaults);
8809             if(!o.concurrent){
8810                 var run = me.beforeFx(o);
8811                 fn.block = o.block;
8812                 getQueue(me.dom.id).push(fn);
8813                 if(run){
8814                     me.nextFx();
8815                 }
8816             }else{
8817                 fn.call(me);
8818             }
8819         }
8820         return me;
8821     },
8822
8823     /* @private */
8824     fxWrap : function(pos, o, vis){ 
8825         var dom = this.dom,
8826             wrap,
8827             wrapXY;
8828         if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){            
8829             if(o.fixPosition){
8830                 wrapXY = fly(dom).getXY();
8831             }
8832             var div = document.createElement("div");
8833             div.style.visibility = vis;
8834             wrap = dom.parentNode.insertBefore(div, dom);
8835             fly(wrap).setPositioning(pos);
8836             if(fly(wrap).isStyle(POSITION, "static")){
8837                 fly(wrap).position("relative");
8838             }
8839             fly(dom).clearPositioning('auto');
8840             fly(wrap).clip();
8841             wrap.appendChild(dom);
8842             if(wrapXY){
8843                 fly(wrap).setXY(wrapXY);
8844             }
8845         }
8846         return wrap;
8847     },
8848
8849     /* @private */
8850     fxUnwrap : function(wrap, pos, o){      
8851         var dom = this.dom;
8852         fly(dom).clearPositioning();
8853         fly(dom).setPositioning(pos);
8854         if(!o.wrap){
8855             var pn = fly(wrap).dom.parentNode;
8856             pn.insertBefore(dom, wrap); 
8857             fly(wrap).remove();
8858         }
8859     },
8860
8861     /* @private */
8862     getFxRestore : function(){
8863         var st = this.dom.style;
8864         return {pos: this.getPositioning(), width: st.width, height : st.height};
8865     },
8866
8867     /* @private */
8868     afterFx : function(o){
8869         var dom = this.dom,
8870             id = dom.id;
8871         if(o.afterStyle){
8872             fly(dom).setStyle(o.afterStyle);            
8873         }
8874         if(o.afterCls){
8875             fly(dom).addClass(o.afterCls);
8876         }
8877         if(o.remove == TRUE){
8878             fly(dom).remove();
8879         }
8880         if(o.callback){
8881             o.callback.call(o.scope, fly(dom));
8882         }
8883         if(!o.concurrent){
8884             getQueue(id).shift();
8885             fly(dom).nextFx();
8886         }
8887     },
8888
8889     /* @private */
8890     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8891         animType = animType || 'run';
8892         opt = opt || {};
8893         var anim = Ext.lib.Anim[animType](
8894                 this.dom, 
8895                 args,
8896                 (opt.duration || defaultDur) || .35,
8897                 (opt.easing || defaultEase) || EASEOUT,
8898                 cb,            
8899                 this
8900             );
8901         opt.anim = anim;
8902         return anim;
8903     }
8904 };
8905
8906 // backwards compat
8907 Ext.Fx.resize = Ext.Fx.scale;
8908
8909 //When included, Ext.Fx is automatically applied to Element so that all basic
8910 //effects are available directly via the Element API
8911 Ext.Element.addMethods(Ext.Fx);
8912 })();
8913 /**
8914  * @class Ext.CompositeElementLite
8915  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8916  * members, or to perform collective actions upon the whole set.</p>
8917  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8918  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8919  * Example:<pre><code>
8920 var els = Ext.select("#some-el div.some-class");
8921 // or select directly from an existing element
8922 var el = Ext.get('some-el');
8923 el.select('div.some-class');
8924
8925 els.setWidth(100); // all elements become 100 width
8926 els.hide(true); // all elements fade out and hide
8927 // or
8928 els.setWidth(100).hide(true);
8929 </code>
8930  */
8931 Ext.CompositeElementLite = function(els, root){
8932     /**
8933      * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8934      * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8935      * to augment the capabilities of the CompositeElementLite class may use it when adding
8936      * methods to the class.</p>
8937      * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8938      * following siblings of selected elements, the code would be</p><code><pre>
8939 Ext.override(Ext.CompositeElementLite, {
8940     nextAll: function() {
8941         var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8942
8943 //      Loop through all elements in this Composite, accumulating
8944 //      an Array of all siblings.
8945         for (i = 0; i < l; i++) {
8946             for (n = els[i].nextSibling; n; n = n.nextSibling) {
8947                 r[++ri] = n;
8948             }
8949         }
8950
8951 //      Add all found siblings to this Composite
8952         return this.add(r);
8953     }
8954 });</pre></code>
8955      * @type Array
8956      * @property elements
8957      */
8958     this.elements = [];
8959     this.add(els, root);
8960     this.el = new Ext.Element.Flyweight();
8961 };
8962
8963 Ext.CompositeElementLite.prototype = {
8964     isComposite: true,
8965
8966     // private
8967     getElement : function(el){
8968         // Set the shared flyweight dom property to the current element
8969         var e = this.el;
8970         e.dom = el;
8971         e.id = el.id;
8972         return e;
8973     },
8974
8975     // private
8976     transformElement : function(el){
8977         return Ext.getDom(el);
8978     },
8979
8980     /**
8981      * Returns the number of elements in this Composite.
8982      * @return Number
8983      */
8984     getCount : function(){
8985         return this.elements.length;
8986     },
8987     /**
8988      * Adds elements to this Composite object.
8989      * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
8990      * @return {CompositeElement} This Composite object.
8991      */
8992     add : function(els, root){
8993         var me = this,
8994             elements = me.elements;
8995         if(!els){
8996             return this;
8997         }
8998         if(typeof els == "string"){
8999             els = Ext.Element.selectorFunction(els, root);
9000         }else if(els.isComposite){
9001             els = els.elements;
9002         }else if(!Ext.isIterable(els)){
9003             els = [els];
9004         }
9005
9006         for(var i = 0, len = els.length; i < len; ++i){
9007             elements.push(me.transformElement(els[i]));
9008         }
9009         return me;
9010     },
9011
9012     invoke : function(fn, args){
9013         var me = this,
9014             els = me.elements,
9015             len = els.length,
9016             e,
9017             i;
9018
9019         for(i = 0; i < len; i++) {
9020             e = els[i];
9021             if(e){
9022                 Ext.Element.prototype[fn].apply(me.getElement(e), args);
9023             }
9024         }
9025         return me;
9026     },
9027     /**
9028      * Returns a flyweight Element of the dom element object at the specified index
9029      * @param {Number} index
9030      * @return {Ext.Element}
9031      */
9032     item : function(index){
9033         var me = this,
9034             el = me.elements[index],
9035             out = null;
9036
9037         if(el){
9038             out = me.getElement(el);
9039         }
9040         return out;
9041     },
9042
9043     // fixes scope with flyweight
9044     addListener : function(eventName, handler, scope, opt){
9045         var els = this.elements,
9046             len = els.length,
9047             i, e;
9048
9049         for(i = 0; i<len; i++) {
9050             e = els[i];
9051             if(e) {
9052                 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
9053             }
9054         }
9055         return this;
9056     },
9057     /**
9058      * <p>Calls the passed function for each element in this composite.</p>
9059      * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
9060      * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
9061      * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
9062      * a reference to the dom node, use el.dom.</b></div></li>
9063      * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9064      * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9065      * </ul>
9066      * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9067      * @return {CompositeElement} this
9068      */
9069     each : function(fn, scope){
9070         var me = this,
9071             els = me.elements,
9072             len = els.length,
9073             i, e;
9074
9075         for(i = 0; i<len; i++) {
9076             e = els[i];
9077             if(e){
9078                 e = this.getElement(e);
9079                 if(fn.call(scope || e, e, me, i) === false){
9080                     break;
9081                 }
9082             }
9083         }
9084         return me;
9085     },
9086
9087     /**
9088     * Clears this Composite and adds the elements passed.
9089     * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9090     * @return {CompositeElement} this
9091     */
9092     fill : function(els){
9093         var me = this;
9094         me.elements = [];
9095         me.add(els);
9096         return me;
9097     },
9098
9099     /**
9100      * Filters this composite to only elements that match the passed selector.
9101      * @param {String/Function} selector A string CSS selector or a comparison function.
9102      * The comparison function will be called with the following arguments:<ul>
9103      * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9104      * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9105      * </ul>
9106      * @return {CompositeElement} this
9107      */
9108     filter : function(selector){
9109         var els = [],
9110             me = this,
9111             elements = me.elements,
9112             fn = Ext.isFunction(selector) ? selector
9113                 : function(el){
9114                     return el.is(selector);
9115                 };
9116
9117
9118         me.each(function(el, self, i){
9119             if(fn(el, i) !== false){
9120                 els[els.length] = me.transformElement(el);
9121             }
9122         });
9123         me.elements = els;
9124         return me;
9125     },
9126
9127     /**
9128      * Find the index of the passed element within the composite collection.
9129      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9130      * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9131      */
9132     indexOf : function(el){
9133         return this.elements.indexOf(this.transformElement(el));
9134     },
9135
9136     /**
9137     * Replaces the specified element with the passed element.
9138     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9139     * to replace.
9140     * @param {Mixed} replacement The id of an element or the Element itself.
9141     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9142     * @return {CompositeElement} this
9143     */
9144     replaceElement : function(el, replacement, domReplace){
9145         var index = !isNaN(el) ? el : this.indexOf(el),
9146             d;
9147         if(index > -1){
9148             replacement = Ext.getDom(replacement);
9149             if(domReplace){
9150                 d = this.elements[index];
9151                 d.parentNode.insertBefore(replacement, d);
9152                 Ext.removeNode(d);
9153             }
9154             this.elements.splice(index, 1, replacement);
9155         }
9156         return this;
9157     },
9158
9159     /**
9160      * Removes all elements.
9161      */
9162     clear : function(){
9163         this.elements = [];
9164     }
9165 };
9166
9167 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9168
9169 (function(){
9170 var fnName,
9171     ElProto = Ext.Element.prototype,
9172     CelProto = Ext.CompositeElementLite.prototype;
9173
9174 for(fnName in ElProto){
9175     if(Ext.isFunction(ElProto[fnName])){
9176         (function(fnName){
9177             CelProto[fnName] = CelProto[fnName] || function(){
9178                 return this.invoke(fnName, arguments);
9179             };
9180         }).call(CelProto, fnName);
9181
9182     }
9183 }
9184 })();
9185
9186 if(Ext.DomQuery){
9187     Ext.Element.selectorFunction = Ext.DomQuery.select;
9188 }
9189
9190 /**
9191  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9192  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9193  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9194  * @param {String/Array} selector The CSS selector or an array of elements
9195  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9196  * @return {CompositeElementLite/CompositeElement}
9197  * @member Ext.Element
9198  * @method select
9199  */
9200 Ext.Element.select = function(selector, root){
9201     var els;
9202     if(typeof selector == "string"){
9203         els = Ext.Element.selectorFunction(selector, root);
9204     }else if(selector.length !== undefined){
9205         els = selector;
9206     }else{
9207         throw "Invalid selector";
9208     }
9209     return new Ext.CompositeElementLite(els);
9210 };
9211 /**
9212  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9213  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9214  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9215  * @param {String/Array} selector The CSS selector or an array of elements
9216  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9217  * @return {CompositeElementLite/CompositeElement}
9218  * @member Ext
9219  * @method select
9220  */
9221 Ext.select = Ext.Element.select;
9222 /**
9223  * @class Ext.CompositeElementLite
9224  */
9225 Ext.apply(Ext.CompositeElementLite.prototype, {
9226     addElements : function(els, root){
9227         if(!els){
9228             return this;
9229         }
9230         if(typeof els == "string"){
9231             els = Ext.Element.selectorFunction(els, root);
9232         }
9233         var yels = this.elements;
9234         Ext.each(els, function(e) {
9235             yels.push(Ext.get(e));
9236         });
9237         return this;
9238     },
9239
9240     /**
9241      * Returns the first Element
9242      * @return {Ext.Element}
9243      */
9244     first : function(){
9245         return this.item(0);
9246     },
9247
9248     /**
9249      * Returns the last Element
9250      * @return {Ext.Element}
9251      */
9252     last : function(){
9253         return this.item(this.getCount()-1);
9254     },
9255
9256     /**
9257      * Returns true if this composite contains the passed element
9258      * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9259      * @return Boolean
9260      */
9261     contains : function(el){
9262         return this.indexOf(el) != -1;
9263     },
9264
9265     /**
9266     * Removes the specified element(s).
9267     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9268     * or an array of any of those.
9269     * @param {Boolean} removeDom (optional) True to also remove the element from the document
9270     * @return {CompositeElement} this
9271     */
9272     removeElement : function(keys, removeDom){
9273         var me = this,
9274             els = this.elements,
9275             el;
9276         Ext.each(keys, function(val){
9277             if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9278                 if(removeDom){
9279                     if(el.dom){
9280                         el.remove();
9281                     }else{
9282                         Ext.removeNode(el);
9283                     }
9284                 }
9285                 els.splice(val, 1);
9286             }
9287         });
9288         return this;
9289     }
9290 });
9291 /**
9292  * @class Ext.CompositeElement
9293  * @extends Ext.CompositeElementLite
9294  * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9295  * members, or to perform collective actions upon the whole set.</p>
9296  * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9297  * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9298  * <p>All methods return <i>this</i> and can be chained.</p>
9299  * Usage:
9300 <pre><code>
9301 var els = Ext.select("#some-el div.some-class", true);
9302 // or select directly from an existing element
9303 var el = Ext.get('some-el');
9304 el.select('div.some-class', true);
9305
9306 els.setWidth(100); // all elements become 100 width
9307 els.hide(true); // all elements fade out and hide
9308 // or
9309 els.setWidth(100).hide(true);
9310 </code></pre>
9311  */
9312 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9313     
9314     constructor : function(els, root){
9315         this.elements = [];
9316         this.add(els, root);
9317     },
9318     
9319     // private
9320     getElement : function(el){
9321         // In this case just return it, since we already have a reference to it
9322         return el;
9323     },
9324     
9325     // private
9326     transformElement : function(el){
9327         return Ext.get(el);
9328     }
9329
9330     /**
9331     * Adds elements to this composite.
9332     * @param {String/Array} els A string CSS selector, an array of elements or an element
9333     * @return {CompositeElement} this
9334     */
9335
9336     /**
9337      * Returns the Element object at the specified index
9338      * @param {Number} index
9339      * @return {Ext.Element}
9340      */
9341
9342     /**
9343      * Iterates each <code>element</code> in this <code>composite</code>
9344      * calling the supplied function using {@link Ext#each}.
9345      * @param {Function} fn The function to be called with each
9346      * <code>element</code>. If the supplied function returns <tt>false</tt>,
9347      * iteration stops. This function is called with the following arguments:
9348      * <div class="mdetail-params"><ul>
9349      * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9350      * in the <code>composite</code></div></li>
9351      * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9352      * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9353      * </ul></div>
9354      * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9355      * Defaults to the <code>element</code> at the current <code>index</code>
9356      * within the composite.
9357      * @return {CompositeElement} this
9358      */
9359 });
9360
9361 /**
9362  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9363  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9364  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9365  * @param {String/Array} selector The CSS selector or an array of elements
9366  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9367  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9368  * @return {CompositeElementLite/CompositeElement}
9369  * @member Ext.Element
9370  * @method select
9371  */
9372 Ext.Element.select = function(selector, unique, root){
9373     var els;
9374     if(typeof selector == "string"){
9375         els = Ext.Element.selectorFunction(selector, root);
9376     }else if(selector.length !== undefined){
9377         els = selector;
9378     }else{
9379         throw "Invalid selector";
9380     }
9381
9382     return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9383 };
9384
9385 /**
9386  * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9387  * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9388  * {@link Ext.CompositeElementLite CompositeElementLite} object.
9389  * @param {String/Array} selector The CSS selector or an array of elements
9390  * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9391  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9392  * @return {CompositeElementLite/CompositeElement}
9393  * @member Ext
9394  * @method select
9395  */
9396 Ext.select = Ext.Element.select;(function(){
9397     var BEFOREREQUEST = "beforerequest",
9398         REQUESTCOMPLETE = "requestcomplete",
9399         REQUESTEXCEPTION = "requestexception",
9400         UNDEFINED = undefined,
9401         LOAD = 'load',
9402         POST = 'POST',
9403         GET = 'GET',
9404         WINDOW = window;
9405
9406     /**
9407      * @class Ext.data.Connection
9408      * @extends Ext.util.Observable
9409      * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9410      * either to a configured URL, or to a URL specified at request time.</p>
9411      * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9412      * the server will be available to the statement immediately following the {@link #request} call.
9413      * To process returned data, use a
9414      * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9415      * in the request options object,
9416      * or an {@link #requestcomplete event listener}.</p>
9417      * <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
9418      * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9419      * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
9420      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9421      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9422      * but removed after the return data has been gathered.</p>
9423      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9424      * server is using JSON to send the return object, then the
9425      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9426      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9427      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9428      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
9429      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9430      * is created containing a <tt>responseText</tt> property in order to conform to the
9431      * requirements of event handlers and callbacks.</p>
9432      * <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>
9433      * and some server technologies (notably JEE) may require some custom processing in order to
9434      * retrieve parameter names and parameter values from the packet content.</p>
9435      * @constructor
9436      * @param {Object} config a configuration object.
9437      */
9438     Ext.data.Connection = function(config){
9439         Ext.apply(this, config);
9440         this.addEvents(
9441             /**
9442              * @event beforerequest
9443              * Fires before a network request is made to retrieve a data object.
9444              * @param {Connection} conn This Connection object.
9445              * @param {Object} options The options config object passed to the {@link #request} method.
9446              */
9447             BEFOREREQUEST,
9448             /**
9449              * @event requestcomplete
9450              * Fires if the request was successfully completed.
9451              * @param {Connection} conn This Connection object.
9452              * @param {Object} response The XHR object containing the response data.
9453              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9454              * for details.
9455              * @param {Object} options The options config object passed to the {@link #request} method.
9456              */
9457             REQUESTCOMPLETE,
9458             /**
9459              * @event requestexception
9460              * Fires if an error HTTP status was returned from the server.
9461              * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9462              * for details of HTTP status codes.
9463              * @param {Connection} conn This Connection object.
9464              * @param {Object} response The XHR object containing the response data.
9465              * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9466              * for details.
9467              * @param {Object} options The options config object passed to the {@link #request} method.
9468              */
9469             REQUESTEXCEPTION
9470         );
9471         Ext.data.Connection.superclass.constructor.call(this);
9472     };
9473
9474     Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9475         /**
9476          * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9477          * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9478          * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9479          */
9480         /**
9481          * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9482          * extra parameters to each request made by this object. (defaults to undefined)
9483          */
9484         /**
9485          * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9486          *  to each request made by this object. (defaults to undefined)
9487          */
9488         /**
9489          * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9490          * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9491          * otherwise, GET will be used.)
9492          */
9493         /**
9494          * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9495          */
9496         timeout : 30000,
9497         /**
9498          * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9499          * @type Boolean
9500          */
9501         autoAbort:false,
9502
9503         /**
9504          * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9505          * @type Boolean
9506          */
9507         disableCaching: true,
9508
9509         /**
9510          * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9511          * through a cache buster. Defaults to '_dc'
9512          * @type String
9513          */
9514         disableCachingParam: '_dc',
9515
9516         /**
9517          * <p>Sends an HTTP request to a remote server.</p>
9518          * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9519          * return before the response has been received. Process any returned data
9520          * in a callback function.</p>
9521          * <pre><code>
9522 Ext.Ajax.request({
9523    url: 'ajax_demo/sample.json',
9524    success: function(response, opts) {
9525       var obj = Ext.decode(response.responseText);
9526       console.dir(obj);
9527    },
9528    failure: function(response, opts) {
9529       console.log('server-side failure with status code ' + response.status);
9530    }
9531 });
9532          * </code></pre>
9533          * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9534          * @param {Object} options An object which may contain the following properties:<ul>
9535          * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9536          * which to send the request, or a function to call which returns a URL string. The scope of the
9537          * function is specified by the <tt>scope</tt> option. Defaults to the configured
9538          * <tt>{@link #url}</tt>.</div></li>
9539          * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9540          * An object containing properties which are used as parameters to the
9541          * request, a url encoded string or a function to call to get either. The scope of the function
9542          * is specified by the <tt>scope</tt> option.</div></li>
9543          * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9544          * for the request. Defaults to the configured method, or if no method was configured,
9545          * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
9546          * the method name is case-sensitive and should be all caps.</div></li>
9547          * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9548          * function to be called upon receipt of the HTTP response. The callback is
9549          * called regardless of success or failure and is passed the following
9550          * parameters:<ul>
9551          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9552          * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9553          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9554          * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9555          * accessing elements of the response.</div></li>
9556          * </ul></div></li>
9557          * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9558          * to be called upon success of the request. The callback is passed the following
9559          * parameters:<ul>
9560          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9561          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9562          * </ul></div></li>
9563          * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9564          * to be called upon failure of the request. The callback is passed the
9565          * following parameters:<ul>
9566          * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9567          * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9568          * </ul></div></li>
9569          * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9570          * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9571          * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9572          * Defaults to the browser window.</div></li>
9573          * <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>
9574          * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
9575          * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
9576          * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9577          * with the <tt>form</tt> option</b>.
9578          * <p>True if the form object is a file upload (will be set automatically if the form was
9579          * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9580          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9581          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9582          * DOM <tt>&lt;form></tt> element temporarily modified to have its
9583          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9584          * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
9585          * but removed after the return data has been gathered.</p>
9586          * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9587          * server is using JSON to send the return object, then the
9588          * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9589          * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9590          * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9591          * is created containing a <tt>responseText</tt> property in order to conform to the
9592          * requirements of event handlers and callbacks.</p>
9593          * <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>
9594          * and some server technologies (notably JEE) may require some custom processing in order to
9595          * retrieve parameter names and parameter values from the packet content.</p>
9596          * </div></li>
9597          * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9598          * headers to set for the request.</div></li>
9599          * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9600          * to use for the post. Note: This will be used instead of params for the post
9601          * data. Any params will be appended to the URL.</div></li>
9602          * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9603          * data to use as the post. Note: This will be used instead of params for the post
9604          * data. Any params will be appended to the URL.</div></li>
9605          * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9606          * to add a unique cache-buster param to GET requests.</div></li>
9607          * </ul></p>
9608          * <p>The options object may also contain any other property which might be needed to perform
9609          * postprocessing in a callback because it is passed to callback functions.</p>
9610          * @return {Number} transactionId The id of the server transaction. This may be used
9611          * to cancel the request.
9612          */
9613         request : function(o){
9614             var me = this;
9615             if(me.fireEvent(BEFOREREQUEST, me, o)){
9616                 if (o.el) {
9617                     if(!Ext.isEmpty(o.indicatorText)){
9618                         me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9619                     }
9620                     if(me.indicatorText) {
9621                         Ext.getDom(o.el).innerHTML = me.indicatorText;
9622                     }
9623                     o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9624                         Ext.getDom(o.el).innerHTML = response.responseText;
9625                     });
9626                 }
9627
9628                 var p = o.params,
9629                     url = o.url || me.url,
9630                     method,
9631                     cb = {success: me.handleResponse,
9632                           failure: me.handleFailure,
9633                           scope: me,
9634                           argument: {options: o},
9635                           timeout : o.timeout || me.timeout
9636                     },
9637                     form,
9638                     serForm;
9639
9640
9641                 if (Ext.isFunction(p)) {
9642                     p = p.call(o.scope||WINDOW, o);
9643                 }
9644
9645                 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9646
9647                 if (Ext.isFunction(url)) {
9648                     url = url.call(o.scope || WINDOW, o);
9649                 }
9650
9651                 if((form = Ext.getDom(o.form))){
9652                     url = url || form.action;
9653                      if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9654                          return me.doFormUpload.call(me, o, p, url);
9655                      }
9656                     serForm = Ext.lib.Ajax.serializeForm(form);
9657                     p = p ? (p + '&' + serForm) : serForm;
9658                 }
9659
9660                 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9661
9662                 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9663                     var dcp = o.disableCachingParam || me.disableCachingParam;
9664                     url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9665                 }
9666
9667                 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9668
9669                 if(o.autoAbort === true || me.autoAbort) {
9670                     me.abort();
9671                 }
9672
9673                 if((method == GET || o.xmlData || o.jsonData) && p){
9674                     url = Ext.urlAppend(url, p);
9675                     p = '';
9676                 }
9677                 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9678             }else{
9679                 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9680             }
9681         },
9682
9683         /**
9684          * Determine whether this object has a request outstanding.
9685          * @param {Number} transactionId (Optional) defaults to the last transaction
9686          * @return {Boolean} True if there is an outstanding request.
9687          */
9688         isLoading : function(transId){
9689             return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9690         },
9691
9692         /**
9693          * Aborts any outstanding request.
9694          * @param {Number} transactionId (Optional) defaults to the last transaction
9695          */
9696         abort : function(transId){
9697             if(transId || this.isLoading()){
9698                 Ext.lib.Ajax.abort(transId || this.transId);
9699             }
9700         },
9701
9702         // private
9703         handleResponse : function(response){
9704             this.transId = false;
9705             var options = response.argument.options;
9706             response.argument = options ? options.argument : null;
9707             this.fireEvent(REQUESTCOMPLETE, this, response, options);
9708             if(options.success){
9709                 options.success.call(options.scope, response, options);
9710             }
9711             if(options.callback){
9712                 options.callback.call(options.scope, options, true, response);
9713             }
9714         },
9715
9716         // private
9717         handleFailure : function(response, e){
9718             this.transId = false;
9719             var options = response.argument.options;
9720             response.argument = options ? options.argument : null;
9721             this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9722             if(options.failure){
9723                 options.failure.call(options.scope, response, options);
9724             }
9725             if(options.callback){
9726                 options.callback.call(options.scope, options, false, response);
9727             }
9728         },
9729
9730         // private
9731         doFormUpload : function(o, ps, url){
9732             var id = Ext.id(),
9733                 doc = document,
9734                 frame = doc.createElement('iframe'),
9735                 form = Ext.getDom(o.form),
9736                 hiddens = [],
9737                 hd,
9738                 encoding = 'multipart/form-data',
9739                 buf = {
9740                     target: form.target,
9741                     method: form.method,
9742                     encoding: form.encoding,
9743                     enctype: form.enctype,
9744                     action: form.action
9745                 };
9746
9747             /*
9748              * Originally this behaviour was modified for Opera 10 to apply the secure URL after
9749              * the frame had been added to the document. It seems this has since been corrected in
9750              * Opera so the behaviour has been reverted, the URL will be set before being added.
9751              */
9752             Ext.fly(frame).set({
9753                 id: id,
9754                 name: id,
9755                 cls: 'x-hidden',
9756                 src: Ext.SSL_SECURE_URL
9757             }); 
9758
9759             doc.body.appendChild(frame);
9760
9761             // This is required so that IE doesn't pop the response up in a new window.
9762             if(Ext.isIE){
9763                document.frames[id].name = id;
9764             }
9765
9766
9767             Ext.fly(form).set({
9768                 target: id,
9769                 method: POST,
9770                 enctype: encoding,
9771                 encoding: encoding,
9772                 action: url || buf.action
9773             });
9774
9775             // add dynamic params
9776             Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9777                 hd = doc.createElement('input');
9778                 Ext.fly(hd).set({
9779                     type: 'hidden',
9780                     value: v,
9781                     name: k
9782                 });
9783                 form.appendChild(hd);
9784                 hiddens.push(hd);
9785             });
9786
9787             function cb(){
9788                 var me = this,
9789                     // bogus response object
9790                     r = {responseText : '',
9791                          responseXML : null,
9792                          argument : o.argument},
9793                     doc,
9794                     firstChild;
9795
9796                 try{
9797                     doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9798                     if(doc){
9799                         if(doc.body){
9800                             if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9801                                 r.responseText = firstChild.value;
9802                             }else{
9803                                 r.responseText = doc.body.innerHTML;
9804                             }
9805                         }
9806                         //in IE the document may still have a body even if returns XML.
9807                         r.responseXML = doc.XMLDocument || doc;
9808                     }
9809                 }
9810                 catch(e) {}
9811
9812                 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9813
9814                 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9815
9816                 function runCallback(fn, scope, args){
9817                     if(Ext.isFunction(fn)){
9818                         fn.apply(scope, args);
9819                     }
9820                 }
9821
9822                 runCallback(o.success, o.scope, [r, o]);
9823                 runCallback(o.callback, o.scope, [o, true, r]);
9824
9825                 if(!me.debugUploads){
9826                     setTimeout(function(){Ext.removeNode(frame);}, 100);
9827                 }
9828             }
9829
9830             Ext.EventManager.on(frame, LOAD, cb, this);
9831             form.submit();
9832
9833             Ext.fly(form).set(buf);
9834             Ext.each(hiddens, function(h) {
9835                 Ext.removeNode(h);
9836             });
9837         }
9838     });
9839 })();
9840
9841 /**
9842  * @class Ext.Ajax
9843  * @extends Ext.data.Connection
9844  * <p>The global Ajax request class that provides a simple way to make Ajax requests
9845  * with maximum flexibility.</p>
9846  * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9847  * and override them at the request function level only if necessary.</p>
9848  * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9849  * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9850  * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9851  * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9852  * </ul></div>
9853  * <pre><code>
9854 // Default headers to pass in every request
9855 Ext.Ajax.defaultHeaders = {
9856     'Powered-By': 'Ext'
9857 };
9858  * </code></pre>
9859  * </p>
9860  * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9861  * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9862  * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9863  * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9864  * </ul></div>
9865  * <pre><code>
9866 // Example: show a spinner during all Ajax requests
9867 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9868 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9869 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9870  * </code></pre>
9871  * </p>
9872  * <p>An example request:</p>
9873  * <pre><code>
9874 // Basic request
9875 Ext.Ajax.{@link Ext.data.Connection#request request}({
9876    url: 'foo.php',
9877    success: someFn,
9878    failure: otherFn,
9879    headers: {
9880        'my-header': 'foo'
9881    },
9882    params: { foo: 'bar' }
9883 });
9884
9885 // Simple ajax form submission
9886 Ext.Ajax.{@link Ext.data.Connection#request request}({
9887     form: 'some-form',
9888     params: 'foo=bar'
9889 });
9890  * </code></pre>
9891  * </p>
9892  * @singleton
9893  */
9894 Ext.Ajax = new Ext.data.Connection({
9895     /**
9896      * @cfg {String} url @hide
9897      */
9898     /**
9899      * @cfg {Object} extraParams @hide
9900      */
9901     /**
9902      * @cfg {Object} defaultHeaders @hide
9903      */
9904     /**
9905      * @cfg {String} method (Optional) @hide
9906      */
9907     /**
9908      * @cfg {Number} timeout (Optional) @hide
9909      */
9910     /**
9911      * @cfg {Boolean} autoAbort (Optional) @hide
9912      */
9913
9914     /**
9915      * @cfg {Boolean} disableCaching (Optional) @hide
9916      */
9917
9918     /**
9919      * @property  disableCaching
9920      * True to add a unique cache-buster param to GET requests. (defaults to true)
9921      * @type Boolean
9922      */
9923     /**
9924      * @property  url
9925      * The default URL to be used for requests to the server. (defaults to undefined)
9926      * If the server receives all requests through one URL, setting this once is easier than
9927      * entering it on every request.
9928      * @type String
9929      */
9930     /**
9931      * @property  extraParams
9932      * An object containing properties which are used as extra parameters to each request made
9933      * by this object (defaults to undefined). Session information and other data that you need
9934      * to pass with each request are commonly put here.
9935      * @type Object
9936      */
9937     /**
9938      * @property  defaultHeaders
9939      * An object containing request headers which are added to each request made by this object
9940      * (defaults to undefined).
9941      * @type Object
9942      */
9943     /**
9944      * @property  method
9945      * The default HTTP method to be used for requests. Note that this is case-sensitive and
9946      * should be all caps (defaults to undefined; if not set but params are present will use
9947      * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9948      * @type String
9949      */
9950     /**
9951      * @property  timeout
9952      * The timeout in milliseconds to be used for requests. (defaults to 30000)
9953      * @type Number
9954      */
9955
9956     /**
9957      * @property  autoAbort
9958      * Whether a new request should abort any pending requests. (defaults to false)
9959      * @type Boolean
9960      */
9961     autoAbort : false,
9962
9963     /**
9964      * Serialize the passed form into a url encoded string
9965      * @param {String/HTMLElement} form
9966      * @return {String}
9967      */
9968     serializeForm : function(form){
9969         return Ext.lib.Ajax.serializeForm(form);
9970     }
9971 });
9972 /**
9973  * @class Ext.Updater
9974  * @extends Ext.util.Observable
9975  * Provides AJAX-style update capabilities for Element objects.  Updater can be used to {@link #update}
9976  * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9977  * {@link Ext.Element Element} on a specific interval.<br><br>
9978  * Usage:<br>
9979  * <pre><code>
9980  * var el = Ext.get("foo"); // Get Ext.Element object
9981  * var mgr = el.getUpdater();
9982  * mgr.update({
9983         url: "http://myserver.com/index.php",
9984         params: {
9985             param1: "foo",
9986             param2: "bar"
9987         }
9988  * });
9989  * ...
9990  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9991  * <br>
9992  * // or directly (returns the same Updater instance)
9993  * var mgr = new Ext.Updater("myElementId");
9994  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9995  * mgr.on("update", myFcnNeedsToKnow);
9996  * <br>
9997  * // short handed call directly from the element object
9998  * Ext.get("foo").load({
9999         url: "bar.php",
10000         scripts: true,
10001         params: "param1=foo&amp;param2=bar",
10002         text: "Loading Foo..."
10003  * });
10004  * </code></pre>
10005  * @constructor
10006  * Create new Updater directly.
10007  * @param {Mixed} el The element to update
10008  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
10009  * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
10010  */
10011 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
10012 function() {
10013     var BEFOREUPDATE = "beforeupdate",
10014         UPDATE = "update",
10015         FAILURE = "failure";
10016
10017     // private
10018     function processSuccess(response){
10019         var me = this;
10020         me.transaction = null;
10021         if (response.argument.form && response.argument.reset) {
10022             try { // put in try/catch since some older FF releases had problems with this
10023                 response.argument.form.reset();
10024             } catch(e){}
10025         }
10026         if (me.loadScripts) {
10027             me.renderer.render(me.el, response, me,
10028                updateComplete.createDelegate(me, [response]));
10029         } else {
10030             me.renderer.render(me.el, response, me);
10031             updateComplete.call(me, response);
10032         }
10033     }
10034
10035     // private
10036     function updateComplete(response, type, success){
10037         this.fireEvent(type || UPDATE, this.el, response);
10038         if(Ext.isFunction(response.argument.callback)){
10039             response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
10040         }
10041     }
10042
10043     // private
10044     function processFailure(response){
10045         updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
10046     }
10047
10048     return {
10049         constructor: function(el, forceNew){
10050             var me = this;
10051             el = Ext.get(el);
10052             if(!forceNew && el.updateManager){
10053                 return el.updateManager;
10054             }
10055             /**
10056              * The Element object
10057              * @type Ext.Element
10058              */
10059             me.el = el;
10060             /**
10061              * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
10062              * @type String
10063              */
10064             me.defaultUrl = null;
10065
10066             me.addEvents(
10067                 /**
10068                  * @event beforeupdate
10069                  * Fired before an update is made, return false from your handler and the update is cancelled.
10070                  * @param {Ext.Element} el
10071                  * @param {String/Object/Function} url
10072                  * @param {String/Object} params
10073                  */
10074                 BEFOREUPDATE,
10075                 /**
10076                  * @event update
10077                  * Fired after successful update is made.
10078                  * @param {Ext.Element} el
10079                  * @param {Object} oResponseObject The response Object
10080                  */
10081                 UPDATE,
10082                 /**
10083                  * @event failure
10084                  * Fired on update failure.
10085                  * @param {Ext.Element} el
10086                  * @param {Object} oResponseObject The response Object
10087                  */
10088                 FAILURE
10089             );
10090
10091             Ext.apply(me, Ext.Updater.defaults);
10092             /**
10093              * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10094              * @property sslBlankUrl
10095              * @type String
10096              */
10097             /**
10098              * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10099              * @property disableCaching
10100              * @type Boolean
10101              */
10102             /**
10103              * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10104              * @property indicatorText
10105              * @type String
10106              */
10107             /**
10108              * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10109              * @property showLoadIndicator
10110              * @type String
10111              */
10112             /**
10113              * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10114              * @property timeout
10115              * @type Number
10116              */
10117             /**
10118              * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10119              * @property loadScripts
10120              * @type Boolean
10121              */
10122
10123             /**
10124              * Transaction object of the current executing transaction, or null if there is no active transaction.
10125              */
10126             me.transaction = null;
10127             /**
10128              * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10129              * @type Function
10130              */
10131             me.refreshDelegate = me.refresh.createDelegate(me);
10132             /**
10133              * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10134              * @type Function
10135              */
10136             me.updateDelegate = me.update.createDelegate(me);
10137             /**
10138              * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10139              * @type Function
10140              */
10141             me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10142
10143             /**
10144              * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10145              */
10146             me.renderer = me.renderer || me.getDefaultRenderer();
10147
10148             Ext.Updater.superclass.constructor.call(me);
10149         },
10150
10151         /**
10152          * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10153          * @param {Object} renderer The object implementing the render() method
10154          */
10155         setRenderer : function(renderer){
10156             this.renderer = renderer;
10157         },
10158
10159         /**
10160          * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10161          * @return {Object}
10162          */
10163         getRenderer : function(){
10164            return this.renderer;
10165         },
10166
10167         /**
10168          * This is an overrideable method which returns a reference to a default
10169          * renderer class if none is specified when creating the Ext.Updater.
10170          * Defaults to {@link Ext.Updater.BasicRenderer}
10171          */
10172         getDefaultRenderer: function() {
10173             return new Ext.Updater.BasicRenderer();
10174         },
10175
10176         /**
10177          * Sets the default URL used for updates.
10178          * @param {String/Function} defaultUrl The url or a function to call to get the url
10179          */
10180         setDefaultUrl : function(defaultUrl){
10181             this.defaultUrl = defaultUrl;
10182         },
10183
10184         /**
10185          * Get the Element this Updater is bound to
10186          * @return {Ext.Element} The element
10187          */
10188         getEl : function(){
10189             return this.el;
10190         },
10191
10192         /**
10193          * Performs an <b>asynchronous</b> request, updating this element with the response.
10194          * If params are specified it uses POST, otherwise it uses GET.<br><br>
10195          * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10196          * will not have been fully updated when the function returns. To post-process the returned
10197          * data, use the callback option, or an <b><code>update</code></b> event handler.
10198          * @param {Object} options A config object containing any of the following options:<ul>
10199          * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10200          * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10201          * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10202          * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10203          * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10204          * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10205          * string, or as an object containing properties which represent parameters,
10206          * or as a function, which returns such an object.</p></li>
10207          * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10208          * any &lt;script&gt; tags embedded in the response text will be extracted
10209          * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10210          * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10211          * <li>callback : <b>Function</b><p class="sub-desc">A function to
10212          * be called when the response from the server arrives. The following
10213          * parameters are passed:<ul>
10214          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10215          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10216          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10217          * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10218          * </p></li>
10219          * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10220          * to execute the callback (The callback's <code>this</code> reference.) If the
10221          * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10222          * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10223          * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10224          * calls.  To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10225          * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10226          * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10227          * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10228          * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...').  To replace the entire div, not
10229          * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10230          * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10231          * requests, this option causes an extra, auto-generated parameter to be appended to the request
10232          * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10233          * <p>
10234          * For example:
10235     <pre><code>
10236     um.update({
10237         url: "your-url.php",
10238         params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10239         callback: yourFunction,
10240         scope: yourObject, //(optional scope)
10241         discardUrl: true,
10242         nocache: true,
10243         text: "Loading...",
10244         timeout: 60,
10245         scripts: false // Save time by avoiding RegExp execution.
10246     });
10247     </code></pre>
10248          */
10249         update : function(url, params, callback, discardUrl){
10250             var me = this,
10251                 cfg,
10252                 callerScope;
10253
10254             if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10255                 if(Ext.isObject(url)){ // must be config object
10256                     cfg = url;
10257                     url = cfg.url;
10258                     params = params || cfg.params;
10259                     callback = callback || cfg.callback;
10260                     discardUrl = discardUrl || cfg.discardUrl;
10261                     callerScope = cfg.scope;
10262                     if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10263                     if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10264                     if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10265                     if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10266                 }
10267                 me.showLoading();
10268
10269                 if(!discardUrl){
10270                     me.defaultUrl = url;
10271                 }
10272                 if(Ext.isFunction(url)){
10273                     url = url.call(me);
10274                 }
10275
10276                 var o = Ext.apply({}, {
10277                     url : url,
10278                     params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10279                     success: processSuccess,
10280                     failure: processFailure,
10281                     scope: me,
10282                     callback: undefined,
10283                     timeout: (me.timeout*1000),
10284                     disableCaching: me.disableCaching,
10285                     argument: {
10286                         "options": cfg,
10287                         "url": url,
10288                         "form": null,
10289                         "callback": callback,
10290                         "scope": callerScope || window,
10291                         "params": params
10292                     }
10293                 }, cfg);
10294
10295                 me.transaction = Ext.Ajax.request(o);
10296             }
10297         },
10298
10299         /**
10300          * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10301          * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10302          * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10303          * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10304          * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10305          * DOM <code>&lt;form></code> element temporarily modified to have its
10306          * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10307          * to a dynamically generated, hidden <code>&lt;iframe></code> which is inserted into the document
10308          * but removed after the return data has been gathered.</p>
10309          * <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>
10310          * and some server technologies (notably JEE) may require some custom processing in order to
10311          * retrieve parameter names and parameter values from the packet content.</p>
10312          * @param {String/HTMLElement} form The form Id or form element
10313          * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10314          * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10315          * @param {Function} callback (optional) Callback when transaction is complete. The following
10316          * parameters are passed:<ul>
10317          * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10318          * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10319          * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10320          */
10321         formUpdate : function(form, url, reset, callback){
10322             var me = this;
10323             if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10324                 if(Ext.isFunction(url)){
10325                     url = url.call(me);
10326                 }
10327                 form = Ext.getDom(form);
10328                 me.transaction = Ext.Ajax.request({
10329                     form: form,
10330                     url:url,
10331                     success: processSuccess,
10332                     failure: processFailure,
10333                     scope: me,
10334                     timeout: (me.timeout*1000),
10335                     argument: {
10336                         "url": url,
10337                         "form": form,
10338                         "callback": callback,
10339                         "reset": reset
10340                     }
10341                 });
10342                 me.showLoading.defer(1, me);
10343             }
10344         },
10345
10346         /**
10347          * Set this element to auto refresh.  Can be canceled by calling {@link #stopAutoRefresh}.
10348          * @param {Number} interval How often to update (in seconds).
10349          * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10350          * supported by {@link #load}, or a function to call to get the url (defaults to the last used url).  Note that while
10351          * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10352          * sepcified as part of a config object passed as this paramter if needed.
10353          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10354          * "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
10355          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10356          * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10357          */
10358         startAutoRefresh : function(interval, url, params, callback, refreshNow){
10359             var me = this;
10360             if(refreshNow){
10361                 me.update(url || me.defaultUrl, params, callback, true);
10362             }
10363             if(me.autoRefreshProcId){
10364                 clearInterval(me.autoRefreshProcId);
10365             }
10366             me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10367         },
10368
10369         /**
10370          * Stop auto refresh on this element.
10371          */
10372         stopAutoRefresh : function(){
10373             if(this.autoRefreshProcId){
10374                 clearInterval(this.autoRefreshProcId);
10375                 delete this.autoRefreshProcId;
10376             }
10377         },
10378
10379         /**
10380          * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10381          */
10382         isAutoRefreshing : function(){
10383            return !!this.autoRefreshProcId;
10384         },
10385
10386         /**
10387          * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10388          * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10389          */
10390         showLoading : function(){
10391             if(this.showLoadIndicator){
10392                 this.el.dom.innerHTML = this.indicatorText;
10393             }
10394         },
10395
10396         /**
10397          * Aborts the currently executing transaction, if any.
10398          */
10399         abort : function(){
10400             if(this.transaction){
10401                 Ext.Ajax.abort(this.transaction);
10402             }
10403         },
10404
10405         /**
10406          * Returns true if an update is in progress, otherwise false.
10407          * @return {Boolean}
10408          */
10409         isUpdating : function(){
10410             return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10411         },
10412
10413         /**
10414          * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10415          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10416          */
10417         refresh : function(callback){
10418             if(this.defaultUrl){
10419                 this.update(this.defaultUrl, null, callback, true);
10420             }
10421         }
10422     }
10423 }());
10424
10425 /**
10426  * @class Ext.Updater.defaults
10427  * The defaults collection enables customizing the default properties of Updater
10428  */
10429 Ext.Updater.defaults = {
10430    /**
10431      * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10432      * @type Number
10433      */
10434     timeout : 30,
10435     /**
10436      * True to append a unique parameter to GET requests to disable caching (defaults to false).
10437      * @type Boolean
10438      */
10439     disableCaching : false,
10440     /**
10441      * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10442      * @type Boolean
10443      */
10444     showLoadIndicator : true,
10445     /**
10446      * Text for loading indicator (defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
10447      * @type String
10448      */
10449     indicatorText : '<div class="loading-indicator">Loading...</div>',
10450      /**
10451      * True to process scripts by default (defaults to false).
10452      * @type Boolean
10453      */
10454     loadScripts : false,
10455     /**
10456     * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10457     * @type String
10458     */
10459     sslBlankUrl : Ext.SSL_SECURE_URL
10460 };
10461
10462
10463 /**
10464  * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10465  * Usage:
10466  * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10467  * @param {Mixed} el The element to update
10468  * @param {String} url The url
10469  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10470  * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10471  * example: {disableCaching:true, indicatorText: "Loading data..."}
10472  * @static
10473  * @deprecated
10474  * @member Ext.Updater
10475  */
10476 Ext.Updater.updateElement = function(el, url, params, options){
10477     var um = Ext.get(el).getUpdater();
10478     Ext.apply(um, options);
10479     um.update(url, params, options ? options.callback : null);
10480 };
10481
10482 /**
10483  * @class Ext.Updater.BasicRenderer
10484  * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10485  * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10486  * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10487  */
10488 Ext.Updater.BasicRenderer = function(){};
10489
10490 Ext.Updater.BasicRenderer.prototype = {
10491     /**
10492      * This method is called when an Ajax response is received, and an Element needs updating.
10493      * @param {Ext.Element} el The element being rendered
10494      * @param {Object} xhr The XMLHttpRequest object
10495      * @param {Updater} updateManager The calling update manager
10496      * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10497      */
10498      render : function(el, response, updateManager, callback){
10499         el.update(response.responseText, updateManager.loadScripts, callback);
10500     }
10501 };/**
10502  * @class Date
10503  *
10504  * The date parsing and formatting syntax contains a subset of
10505  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10506  * supported will provide results equivalent to their PHP versions.
10507  *
10508  * The following is a list of all currently supported formats:
10509  * <pre>
10510 Format  Description                                                               Example returned values
10511 ------  -----------------------------------------------------------------------   -----------------------
10512   d     Day of the month, 2 digits with leading zeros                             01 to 31
10513   D     A short textual representation of the day of the week                     Mon to Sun
10514   j     Day of the month without leading zeros                                    1 to 31
10515   l     A full textual representation of the day of the week                      Sunday to Saturday
10516   N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
10517   S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
10518   w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
10519   z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
10520   W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
10521   F     A full textual representation of a month, such as January or March        January to December
10522   m     Numeric representation of a month, with leading zeros                     01 to 12
10523   M     A short textual representation of a month                                 Jan to Dec
10524   n     Numeric representation of a month, without leading zeros                  1 to 12
10525   t     Number of days in the given month                                         28 to 31
10526   L     Whether it's a leap year                                                  1 if it is a leap year, 0 otherwise.
10527   o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
10528         belongs to the previous or next year, that year is used instead)
10529   Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
10530   y     A two digit representation of a year                                      Examples: 99 or 03
10531   a     Lowercase Ante meridiem and Post meridiem                                 am or pm
10532   A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
10533   g     12-hour format of an hour without leading zeros                           1 to 12
10534   G     24-hour format of an hour without leading zeros                           0 to 23
10535   h     12-hour format of an hour with leading zeros                              01 to 12
10536   H     24-hour format of an hour with leading zeros                              00 to 23
10537   i     Minutes, with leading zeros                                               00 to 59
10538   s     Seconds, with leading zeros                                               00 to 59
10539   u     Decimal fraction of a second                                              Examples:
10540         (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
10541                                                                                   100 (i.e. 0.100s) or
10542                                                                                   999 (i.e. 0.999s) or
10543                                                                                   999876543210 (i.e. 0.999876543210s)
10544   O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
10545   P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
10546   T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
10547   Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
10548   c     ISO 8601 date
10549         Notes:                                                                    Examples:
10550         1) If unspecified, the month / day defaults to the current month / day,   1991 or
10551            the time defaults to midnight, while the timezone defaults to the      1992-10 or
10552            browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10553            and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
10554            are optional.                                                          1995-07-18T17:21:28-02:00 or
10555         2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
10556            least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
10557            of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
10558         Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
10559         date-time granularity which are supported, or see                         2000-02-13T21:25:33
10560         http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
10561   U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
10562   M$    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10563                                                                                   \/Date(1238606590509+0800)\/
10564 </pre>
10565  *
10566  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10567  * <pre><code>
10568 // Sample date:
10569 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10570
10571 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10572 document.write(dt.format('Y-m-d'));                           // 2007-01-10
10573 document.write(dt.format('F j, Y, g:i a'));                   // January 10, 2007, 3:05 pm
10574 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
10575 </code></pre>
10576  *
10577  * Here are some standard date/time patterns that you might find helpful.  They
10578  * are not part of the source of Date.js, but to use them you can simply copy this
10579  * block of code into any script that is included after Date.js and they will also become
10580  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
10581  * <pre><code>
10582 Date.patterns = {
10583     ISO8601Long:"Y-m-d H:i:s",
10584     ISO8601Short:"Y-m-d",
10585     ShortDate: "n/j/Y",
10586     LongDate: "l, F d, Y",
10587     FullDateTime: "l, F d, Y g:i:s A",
10588     MonthDay: "F d",
10589     ShortTime: "g:i A",
10590     LongTime: "g:i:s A",
10591     SortableDateTime: "Y-m-d\\TH:i:s",
10592     UniversalSortableDateTime: "Y-m-d H:i:sO",
10593     YearMonth: "F, Y"
10594 };
10595 </code></pre>
10596  *
10597  * Example usage:
10598  * <pre><code>
10599 var dt = new Date();
10600 document.write(dt.format(Date.patterns.ShortDate));
10601 </code></pre>
10602  * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10603  * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10604  */
10605
10606 /*
10607  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10608  * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10609  * They generate precompiled functions from format patterns instead of parsing and
10610  * processing each pattern every time a date is formatted. These functions are available
10611  * on every Date object.
10612  */
10613
10614 (function() {
10615
10616 /**
10617  * Global flag which determines if strict date parsing should be used.
10618  * Strict date parsing will not roll-over invalid dates, which is the
10619  * default behaviour of javascript Date objects.
10620  * (see {@link #parseDate} for more information)
10621  * Defaults to <tt>false</tt>.
10622  * @static
10623  * @type Boolean
10624 */
10625 Date.useStrict = false;
10626
10627
10628 // create private copy of Ext's String.format() method
10629 // - to remove unnecessary dependency
10630 // - to resolve namespace conflict with M$-Ajax's implementation
10631 function xf(format) {
10632     var args = Array.prototype.slice.call(arguments, 1);
10633     return format.replace(/\{(\d+)\}/g, function(m, i) {
10634         return args[i];
10635     });
10636 }
10637
10638
10639 // private
10640 Date.formatCodeToRegex = function(character, currentGroup) {
10641     // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10642     var p = Date.parseCodes[character];
10643
10644     if (p) {
10645       p = typeof p == 'function'? p() : p;
10646       Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10647     }
10648
10649     return p ? Ext.applyIf({
10650       c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10651     }, p) : {
10652         g:0,
10653         c:null,
10654         s:Ext.escapeRe(character) // treat unrecognised characters as literals
10655     }
10656 };
10657
10658 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10659 var $f = Date.formatCodeToRegex;
10660
10661 Ext.apply(Date, {
10662     /**
10663      * <p>An object hash in which each property is a date parsing function. The property name is the
10664      * format string which that function parses.</p>
10665      * <p>This object is automatically populated with date parsing functions as
10666      * date formats are requested for Ext standard formatting strings.</p>
10667      * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10668      * may be used as a format string to {@link #parseDate}.<p>
10669      * <p>Example:</p><pre><code>
10670 Date.parseFunctions['x-date-format'] = myDateParser;
10671 </code></pre>
10672      * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10673      * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10674      * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10675      * (i.e. prevent javascript Date "rollover") (The default must be false).
10676      * Invalid date strings should return null when parsed.</div></li>
10677      * </ul></div></p>
10678      * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10679      * formatting function must be placed into the {@link #formatFunctions} property.
10680      * @property parseFunctions
10681      * @static
10682      * @type Object
10683      */
10684     parseFunctions: {
10685         "M$": function(input, strict) {
10686             // note: the timezone offset is ignored since the M$ Ajax server sends
10687             // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10688             var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10689             var r = (input || '').match(re);
10690             return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10691         }
10692     },
10693     parseRegexes: [],
10694
10695     /**
10696      * <p>An object hash in which each property is a date formatting function. The property name is the
10697      * format string which corresponds to the produced formatted date string.</p>
10698      * <p>This object is automatically populated with date formatting functions as
10699      * date formats are requested for Ext standard formatting strings.</p>
10700      * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10701      * may be used as a format string to {@link #format}. Example:</p><pre><code>
10702 Date.formatFunctions['x-date-format'] = myDateFormatter;
10703 </code></pre>
10704      * <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>
10705      * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10706      * </ul></div></p>
10707      * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10708      * parsing function must be placed into the {@link #parseFunctions} property.
10709      * @property formatFunctions
10710      * @static
10711      * @type Object
10712      */
10713     formatFunctions: {
10714         "M$": function() {
10715             // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10716             return '\\/Date(' + this.getTime() + ')\\/';
10717         }
10718     },
10719
10720     y2kYear : 50,
10721
10722     /**
10723      * Date interval constant
10724      * @static
10725      * @type String
10726      */
10727     MILLI : "ms",
10728
10729     /**
10730      * Date interval constant
10731      * @static
10732      * @type String
10733      */
10734     SECOND : "s",
10735
10736     /**
10737      * Date interval constant
10738      * @static
10739      * @type String
10740      */
10741     MINUTE : "mi",
10742
10743     /** Date interval constant
10744      * @static
10745      * @type String
10746      */
10747     HOUR : "h",
10748
10749     /**
10750      * Date interval constant
10751      * @static
10752      * @type String
10753      */
10754     DAY : "d",
10755
10756     /**
10757      * Date interval constant
10758      * @static
10759      * @type String
10760      */
10761     MONTH : "mo",
10762
10763     /**
10764      * Date interval constant
10765      * @static
10766      * @type String
10767      */
10768     YEAR : "y",
10769
10770     /**
10771      * <p>An object hash containing default date values used during date parsing.</p>
10772      * <p>The following properties are available:<div class="mdetail-params"><ul>
10773      * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10774      * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10775      * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10776      * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10777      * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10778      * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10779      * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10780      * </ul></div></p>
10781      * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10782      * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10783      * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10784      * It is the responsiblity of the developer to account for this.</b></p>
10785      * Example Usage:
10786      * <pre><code>
10787 // set default day value to the first day of the month
10788 Date.defaults.d = 1;
10789
10790 // parse a February date string containing only year and month values.
10791 // setting the default day value to 1 prevents weird date rollover issues
10792 // when attempting to parse the following date string on, for example, March 31st 2009.
10793 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10794 </code></pre>
10795      * @property defaults
10796      * @static
10797      * @type Object
10798      */
10799     defaults: {},
10800
10801     /**
10802      * An array of textual day names.
10803      * Override these values for international dates.
10804      * Example:
10805      * <pre><code>
10806 Date.dayNames = [
10807     'SundayInYourLang',
10808     'MondayInYourLang',
10809     ...
10810 ];
10811 </code></pre>
10812      * @type Array
10813      * @static
10814      */
10815     dayNames : [
10816         "Sunday",
10817         "Monday",
10818         "Tuesday",
10819         "Wednesday",
10820         "Thursday",
10821         "Friday",
10822         "Saturday"
10823     ],
10824
10825     /**
10826      * An array of textual month names.
10827      * Override these values for international dates.
10828      * Example:
10829      * <pre><code>
10830 Date.monthNames = [
10831     'JanInYourLang',
10832     'FebInYourLang',
10833     ...
10834 ];
10835 </code></pre>
10836      * @type Array
10837      * @static
10838      */
10839     monthNames : [
10840         "January",
10841         "February",
10842         "March",
10843         "April",
10844         "May",
10845         "June",
10846         "July",
10847         "August",
10848         "September",
10849         "October",
10850         "November",
10851         "December"
10852     ],
10853
10854     /**
10855      * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10856      * Override these values for international dates.
10857      * Example:
10858      * <pre><code>
10859 Date.monthNumbers = {
10860     'ShortJanNameInYourLang':0,
10861     'ShortFebNameInYourLang':1,
10862     ...
10863 };
10864 </code></pre>
10865      * @type Object
10866      * @static
10867      */
10868     monthNumbers : {
10869         Jan:0,
10870         Feb:1,
10871         Mar:2,
10872         Apr:3,
10873         May:4,
10874         Jun:5,
10875         Jul:6,
10876         Aug:7,
10877         Sep:8,
10878         Oct:9,
10879         Nov:10,
10880         Dec:11
10881     },
10882
10883     /**
10884      * Get the short month name for the given month number.
10885      * Override this function for international dates.
10886      * @param {Number} month A zero-based javascript month number.
10887      * @return {String} The short month name.
10888      * @static
10889      */
10890     getShortMonthName : function(month) {
10891         return Date.monthNames[month].substring(0, 3);
10892     },
10893
10894     /**
10895      * Get the short day name for the given day number.
10896      * Override this function for international dates.
10897      * @param {Number} day A zero-based javascript day number.
10898      * @return {String} The short day name.
10899      * @static
10900      */
10901     getShortDayName : function(day) {
10902         return Date.dayNames[day].substring(0, 3);
10903     },
10904
10905     /**
10906      * Get the zero-based javascript month number for the given short/full month name.
10907      * Override this function for international dates.
10908      * @param {String} name The short/full month name.
10909      * @return {Number} The zero-based javascript month number.
10910      * @static
10911      */
10912     getMonthNumber : function(name) {
10913         // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10914         return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10915     },
10916
10917     /**
10918      * The base format-code to formatting-function hashmap used by the {@link #format} method.
10919      * Formatting functions are strings (or functions which return strings) which
10920      * will return the appropriate value when evaluated in the context of the Date object
10921      * from which the {@link #format} method is called.
10922      * Add to / override these mappings for custom date formatting.
10923      * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10924      * Example:
10925      * <pre><code>
10926 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10927 (new Date()).format("X"); // returns the current day of the month
10928 </code></pre>
10929      * @type Object
10930      * @static
10931      */
10932     formatCodes : {
10933         d: "String.leftPad(this.getDate(), 2, '0')",
10934         D: "Date.getShortDayName(this.getDay())", // get localised short day name
10935         j: "this.getDate()",
10936         l: "Date.dayNames[this.getDay()]",
10937         N: "(this.getDay() ? this.getDay() : 7)",
10938         S: "this.getSuffix()",
10939         w: "this.getDay()",
10940         z: "this.getDayOfYear()",
10941         W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10942         F: "Date.monthNames[this.getMonth()]",
10943         m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10944         M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10945         n: "(this.getMonth() + 1)",
10946         t: "this.getDaysInMonth()",
10947         L: "(this.isLeapYear() ? 1 : 0)",
10948         o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10949         Y: "this.getFullYear()",
10950         y: "('' + this.getFullYear()).substring(2, 4)",
10951         a: "(this.getHours() < 12 ? 'am' : 'pm')",
10952         A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10953         g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10954         G: "this.getHours()",
10955         h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10956         H: "String.leftPad(this.getHours(), 2, '0')",
10957         i: "String.leftPad(this.getMinutes(), 2, '0')",
10958         s: "String.leftPad(this.getSeconds(), 2, '0')",
10959         u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10960         O: "this.getGMTOffset()",
10961         P: "this.getGMTOffset(true)",
10962         T: "this.getTimezone()",
10963         Z: "(this.getTimezoneOffset() * -60)",
10964
10965         c: function() { // ISO-8601 -- GMT format
10966             for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10967                 var e = c.charAt(i);
10968                 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10969             }
10970             return code.join(" + ");
10971         },
10972         /*
10973         c: function() { // ISO-8601 -- UTC format
10974             return [
10975               "this.getUTCFullYear()", "'-'",
10976               "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10977               "String.leftPad(this.getUTCDate(), 2, '0')",
10978               "'T'",
10979               "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10980               "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10981               "String.leftPad(this.getUTCSeconds(), 2, '0')",
10982               "'Z'"
10983             ].join(" + ");
10984         },
10985         */
10986
10987         U: "Math.round(this.getTime() / 1000)"
10988     },
10989
10990     /**
10991      * Checks if the passed Date parameters will cause a javascript Date "rollover".
10992      * @param {Number} year 4-digit year
10993      * @param {Number} month 1-based month-of-year
10994      * @param {Number} day Day of month
10995      * @param {Number} hour (optional) Hour
10996      * @param {Number} minute (optional) Minute
10997      * @param {Number} second (optional) Second
10998      * @param {Number} millisecond (optional) Millisecond
10999      * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
11000      * @static
11001      */
11002     isValid : function(y, m, d, h, i, s, ms) {
11003         // setup defaults
11004         h = h || 0;
11005         i = i || 0;
11006         s = s || 0;
11007         ms = ms || 0;
11008
11009         var dt = new Date(y, m - 1, d, h, i, s, ms);
11010
11011         return y == dt.getFullYear() &&
11012             m == dt.getMonth() + 1 &&
11013             d == dt.getDate() &&
11014             h == dt.getHours() &&
11015             i == dt.getMinutes() &&
11016             s == dt.getSeconds() &&
11017             ms == dt.getMilliseconds();
11018     },
11019
11020     /**
11021      * Parses the passed string using the specified date format.
11022      * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
11023      * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
11024      * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
11025      * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
11026      * Keep in mind that the input date string must precisely match the specified format string
11027      * in order for the parse operation to be successful (failed parse operations return a null value).
11028      * <p>Example:</p><pre><code>
11029 //dt = Fri May 25 2007 (current date)
11030 var dt = new Date();
11031
11032 //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
11033 dt = Date.parseDate("2006", "Y");
11034
11035 //dt = Sun Jan 15 2006 (all date parts specified)
11036 dt = Date.parseDate("2006-01-15", "Y-m-d");
11037
11038 //dt = Sun Jan 15 2006 15:20:01
11039 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
11040
11041 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
11042 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
11043 </code></pre>
11044      * @param {String} input The raw date string.
11045      * @param {String} format The expected date string format.
11046      * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
11047                         (defaults to false). Invalid date strings will return null when parsed.
11048      * @return {Date} The parsed Date.
11049      * @static
11050      */
11051     parseDate : function(input, format, strict) {
11052         var p = Date.parseFunctions;
11053         if (p[format] == null) {
11054             Date.createParser(format);
11055         }
11056         return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
11057     },
11058
11059     // private
11060     getFormatCode : function(character) {
11061         var f = Date.formatCodes[character];
11062
11063         if (f) {
11064           f = typeof f == 'function'? f() : f;
11065           Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11066         }
11067
11068         // note: unknown characters are treated as literals
11069         return f || ("'" + String.escape(character) + "'");
11070     },
11071
11072     // private
11073     createFormat : function(format) {
11074         var code = [],
11075             special = false,
11076             ch = '';
11077
11078         for (var i = 0; i < format.length; ++i) {
11079             ch = format.charAt(i);
11080             if (!special && ch == "\\") {
11081                 special = true;
11082             } else if (special) {
11083                 special = false;
11084                 code.push("'" + String.escape(ch) + "'");
11085             } else {
11086                 code.push(Date.getFormatCode(ch))
11087             }
11088         }
11089         Date.formatFunctions[format] = new Function("return " + code.join('+'));
11090     },
11091
11092     // private
11093     createParser : function() {
11094         var code = [
11095             "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11096                 "def = Date.defaults,",
11097                 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11098
11099             "if(results){",
11100                 "{1}",
11101
11102                 "if(u != null){", // i.e. unix time is defined
11103                     "v = new Date(u * 1000);", // give top priority to UNIX time
11104                 "}else{",
11105                     // create Date object representing midnight of the current day;
11106                     // this will provide us with our date defaults
11107                     // (note: clearTime() handles Daylight Saving Time automatically)
11108                     "dt = (new Date()).clearTime();",
11109
11110                     // date calculations (note: these calculations create a dependency on Ext.num())
11111                     "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11112                     "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11113                     "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11114
11115                     // time calculations (note: these calculations create a dependency on Ext.num())
11116                     "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11117                     "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11118                     "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11119                     "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11120
11121                     "if(z >= 0 && y >= 0){",
11122                         // both the year and zero-based day of year are defined and >= 0.
11123                         // these 2 values alone provide sufficient info to create a full date object
11124
11125                         // create Date object representing January 1st for the given year
11126                         "v = new Date(y, 0, 1, h, i, s, ms);",
11127
11128                         // then add day of year, checking for Date "rollover" if necessary
11129                         "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11130                     "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11131                         "v = null;", // invalid date, so return null
11132                     "}else{",
11133                         // plain old Date object
11134                         "v = new Date(y, m, d, h, i, s, ms);",
11135                     "}",
11136                 "}",
11137             "}",
11138
11139             "if(v){",
11140                 // favour UTC offset over GMT offset
11141                 "if(zz != null){",
11142                     // reset to UTC, then add offset
11143                     "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11144                 "}else if(o){",
11145                     // reset to GMT, then add offset
11146                     "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11147                 "}",
11148             "}",
11149
11150             "return v;"
11151         ].join('\n');
11152
11153         return function(format) {
11154             var regexNum = Date.parseRegexes.length,
11155                 currentGroup = 1,
11156                 calc = [],
11157                 regex = [],
11158                 special = false,
11159                 ch = "";
11160
11161             for (var i = 0; i < format.length; ++i) {
11162                 ch = format.charAt(i);
11163                 if (!special && ch == "\\") {
11164                     special = true;
11165                 } else if (special) {
11166                     special = false;
11167                     regex.push(String.escape(ch));
11168                 } else {
11169                     var obj = $f(ch, currentGroup);
11170                     currentGroup += obj.g;
11171                     regex.push(obj.s);
11172                     if (obj.g && obj.c) {
11173                         calc.push(obj.c);
11174                     }
11175                 }
11176             }
11177
11178             Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11179             Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11180         }
11181     }(),
11182
11183     // private
11184     parseCodes : {
11185         /*
11186          * Notes:
11187          * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11188          * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11189          * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11190          */
11191         d: {
11192             g:1,
11193             c:"d = parseInt(results[{0}], 10);\n",
11194             s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11195         },
11196         j: {
11197             g:1,
11198             c:"d = parseInt(results[{0}], 10);\n",
11199             s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11200         },
11201         D: function() {
11202             for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11203             return {
11204                 g:0,
11205                 c:null,
11206                 s:"(?:" + a.join("|") +")"
11207             }
11208         },
11209         l: function() {
11210             return {
11211                 g:0,
11212                 c:null,
11213                 s:"(?:" + Date.dayNames.join("|") + ")"
11214             }
11215         },
11216         N: {
11217             g:0,
11218             c:null,
11219             s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11220         },
11221         S: {
11222             g:0,
11223             c:null,
11224             s:"(?:st|nd|rd|th)"
11225         },
11226         w: {
11227             g:0,
11228             c:null,
11229             s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11230         },
11231         z: {
11232             g:1,
11233             c:"z = parseInt(results[{0}], 10);\n",
11234             s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11235         },
11236         W: {
11237             g:0,
11238             c:null,
11239             s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11240         },
11241         F: function() {
11242             return {
11243                 g:1,
11244                 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11245                 s:"(" + Date.monthNames.join("|") + ")"
11246             }
11247         },
11248         M: function() {
11249             for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11250             return Ext.applyIf({
11251                 s:"(" + a.join("|") + ")"
11252             }, $f("F"));
11253         },
11254         m: {
11255             g:1,
11256             c:"m = parseInt(results[{0}], 10) - 1;\n",
11257             s:"(\\d{2})" // month number with leading zeros (01 - 12)
11258         },
11259         n: {
11260             g:1,
11261             c:"m = parseInt(results[{0}], 10) - 1;\n",
11262             s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11263         },
11264         t: {
11265             g:0,
11266             c:null,
11267             s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11268         },
11269         L: {
11270             g:0,
11271             c:null,
11272             s:"(?:1|0)"
11273         },
11274         o: function() {
11275             return $f("Y");
11276         },
11277         Y: {
11278             g:1,
11279             c:"y = parseInt(results[{0}], 10);\n",
11280             s:"(\\d{4})" // 4-digit year
11281         },
11282         y: {
11283             g:1,
11284             c:"var ty = parseInt(results[{0}], 10);\n"
11285                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11286             s:"(\\d{1,2})"
11287         },
11288         a: {
11289             g:1,
11290             c:"if (results[{0}] == 'am') {\n"
11291                 + "if (!h || h == 12) { h = 0; }\n"
11292                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11293             s:"(am|pm)"
11294         },
11295         A: {
11296             g:1,
11297             c:"if (results[{0}] == 'AM') {\n"
11298                 + "if (!h || h == 12) { h = 0; }\n"
11299                 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11300             s:"(AM|PM)"
11301         },
11302         g: function() {
11303             return $f("G");
11304         },
11305         G: {
11306             g:1,
11307             c:"h = parseInt(results[{0}], 10);\n",
11308             s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11309         },
11310         h: function() {
11311             return $f("H");
11312         },
11313         H: {
11314             g:1,
11315             c:"h = parseInt(results[{0}], 10);\n",
11316             s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
11317         },
11318         i: {
11319             g:1,
11320             c:"i = parseInt(results[{0}], 10);\n",
11321             s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11322         },
11323         s: {
11324             g:1,
11325             c:"s = parseInt(results[{0}], 10);\n",
11326             s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11327         },
11328         u: {
11329             g:1,
11330             c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11331             s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11332         },
11333         O: {
11334             g:1,
11335             c:[
11336                 "o = results[{0}];",
11337                 "var sn = o.substring(0,1),", // get + / - sign
11338                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11339                     "mn = o.substring(3,5) % 60;", // get minutes
11340                 "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
11341             ].join("\n"),
11342             s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11343         },
11344         P: {
11345             g:1,
11346             c:[
11347                 "o = results[{0}];",
11348                 "var sn = o.substring(0,1),", // get + / - sign
11349                     "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11350                     "mn = o.substring(4,6) % 60;", // get minutes
11351                 "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
11352             ].join("\n"),
11353             s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11354         },
11355         T: {
11356             g:0,
11357             c:null,
11358             s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11359         },
11360         Z: {
11361             g:1,
11362             c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11363                   + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11364             s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11365         },
11366         c: function() {
11367             var calc = [],
11368                 arr = [
11369                     $f("Y", 1), // year
11370                     $f("m", 2), // month
11371                     $f("d", 3), // day
11372                     $f("h", 4), // hour
11373                     $f("i", 5), // minute
11374                     $f("s", 6), // second
11375                     {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)
11376                     {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
11377                         "if(results[8]) {", // timezone specified
11378                             "if(results[8] == 'Z'){",
11379                                 "zz = 0;", // UTC
11380                             "}else if (results[8].indexOf(':') > -1){",
11381                                 $f("P", 8).c, // timezone offset with colon separator
11382                             "}else{",
11383                                 $f("O", 8).c, // timezone offset without colon separator
11384                             "}",
11385                         "}"
11386                     ].join('\n')}
11387                 ];
11388
11389             for (var i = 0, l = arr.length; i < l; ++i) {
11390                 calc.push(arr[i].c);
11391             }
11392
11393             return {
11394                 g:1,
11395                 c:calc.join(""),
11396                 s:[
11397                     arr[0].s, // year (required)
11398                     "(?:", "-", arr[1].s, // month (optional)
11399                         "(?:", "-", arr[2].s, // day (optional)
11400                             "(?:",
11401                                 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11402                                 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
11403                                 "(?::", arr[5].s, ")?", // seconds (optional)
11404                                 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11405                                 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11406                             ")?",
11407                         ")?",
11408                     ")?"
11409                 ].join("")
11410             }
11411         },
11412         U: {
11413             g:1,
11414             c:"u = parseInt(results[{0}], 10);\n",
11415             s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11416         }
11417     }
11418 });
11419
11420 }());
11421
11422 Ext.apply(Date.prototype, {
11423     // private
11424     dateFormat : function(format) {
11425         if (Date.formatFunctions[format] == null) {
11426             Date.createFormat(format);
11427         }
11428         return Date.formatFunctions[format].call(this);
11429     },
11430
11431     /**
11432      * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11433      *
11434      * Note: The date string returned by the javascript Date object's toString() method varies
11435      * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11436      * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11437      * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11438      * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11439      * from the GMT offset portion of the date string.
11440      * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11441      */
11442     getTimezone : function() {
11443         // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11444         //
11445         // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11446         // 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)
11447         // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11448         // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11449         // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11450         //
11451         // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11452         // step 1: (?:\((.*)\) -- find timezone in parentheses
11453         // 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
11454         // step 3: remove all non uppercase characters found in step 1 and 2
11455         return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11456     },
11457
11458     /**
11459      * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11460      * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11461      * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11462      */
11463     getGMTOffset : function(colon) {
11464         return (this.getTimezoneOffset() > 0 ? "-" : "+")
11465             + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11466             + (colon ? ":" : "")
11467             + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11468     },
11469
11470     /**
11471      * Get the numeric day number of the year, adjusted for leap year.
11472      * @return {Number} 0 to 364 (365 in leap years).
11473      */
11474     getDayOfYear: function() {
11475         var num = 0,
11476             d = this.clone(),
11477             m = this.getMonth(),
11478             i;
11479
11480         for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11481             num += d.getDaysInMonth();
11482         }
11483         return num + this.getDate() - 1;
11484     },
11485
11486     /**
11487      * Get the numeric ISO-8601 week number of the year.
11488      * (equivalent to the format specifier 'W', but without a leading zero).
11489      * @return {Number} 1 to 53
11490      */
11491     getWeekOfYear : function() {
11492         // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11493         var ms1d = 864e5, // milliseconds in a day
11494             ms7d = 7 * ms1d; // milliseconds in a week
11495
11496         return function() { // return a closure so constants get calculated only once
11497             var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11498                 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11499                 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11500
11501             return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11502         }
11503     }(),
11504
11505     /**
11506      * Checks if the current date falls within a leap year.
11507      * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11508      */
11509     isLeapYear : function() {
11510         var year = this.getFullYear();
11511         return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11512     },
11513
11514     /**
11515      * Get the first day of the current month, adjusted for leap year.  The returned value
11516      * is the numeric day index within the week (0-6) which can be used in conjunction with
11517      * the {@link #monthNames} array to retrieve the textual day name.
11518      * Example:
11519      * <pre><code>
11520 var dt = new Date('1/10/2007');
11521 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11522 </code></pre>
11523      * @return {Number} The day number (0-6).
11524      */
11525     getFirstDayOfMonth : function() {
11526         var day = (this.getDay() - (this.getDate() - 1)) % 7;
11527         return (day < 0) ? (day + 7) : day;
11528     },
11529
11530     /**
11531      * Get the last day of the current month, adjusted for leap year.  The returned value
11532      * is the numeric day index within the week (0-6) which can be used in conjunction with
11533      * the {@link #monthNames} array to retrieve the textual day name.
11534      * Example:
11535      * <pre><code>
11536 var dt = new Date('1/10/2007');
11537 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11538 </code></pre>
11539      * @return {Number} The day number (0-6).
11540      */
11541     getLastDayOfMonth : function() {
11542         return this.getLastDateOfMonth().getDay();
11543     },
11544
11545
11546     /**
11547      * Get the date of the first day of the month in which this date resides.
11548      * @return {Date}
11549      */
11550     getFirstDateOfMonth : function() {
11551         return new Date(this.getFullYear(), this.getMonth(), 1);
11552     },
11553
11554     /**
11555      * Get the date of the last day of the month in which this date resides.
11556      * @return {Date}
11557      */
11558     getLastDateOfMonth : function() {
11559         return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11560     },
11561
11562     /**
11563      * Get the number of days in the current month, adjusted for leap year.
11564      * @return {Number} The number of days in the month.
11565      */
11566     getDaysInMonth: function() {
11567         var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11568
11569         return function() { // return a closure for efficiency
11570             var m = this.getMonth();
11571
11572             return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11573         }
11574     }(),
11575
11576     /**
11577      * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11578      * @return {String} 'st, 'nd', 'rd' or 'th'.
11579      */
11580     getSuffix : function() {
11581         switch (this.getDate()) {
11582             case 1:
11583             case 21:
11584             case 31:
11585                 return "st";
11586             case 2:
11587             case 22:
11588                 return "nd";
11589             case 3:
11590             case 23:
11591                 return "rd";
11592             default:
11593                 return "th";
11594         }
11595     },
11596
11597     /**
11598      * Creates and returns a new Date instance with the exact same date value as the called instance.
11599      * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11600      * variable will also be changed.  When the intention is to create a new variable that will not
11601      * modify the original instance, you should create a clone.
11602      *
11603      * Example of correctly cloning a date:
11604      * <pre><code>
11605 //wrong way:
11606 var orig = new Date('10/1/2006');
11607 var copy = orig;
11608 copy.setDate(5);
11609 document.write(orig);  //returns 'Thu Oct 05 2006'!
11610
11611 //correct way:
11612 var orig = new Date('10/1/2006');
11613 var copy = orig.clone();
11614 copy.setDate(5);
11615 document.write(orig);  //returns 'Thu Oct 01 2006'
11616 </code></pre>
11617      * @return {Date} The new Date instance.
11618      */
11619     clone : function() {
11620         return new Date(this.getTime());
11621     },
11622
11623     /**
11624      * Checks if the current date is affected by Daylight Saving Time (DST).
11625      * @return {Boolean} True if the current date is affected by DST.
11626      */
11627     isDST : function() {
11628         // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11629         // courtesy of @geoffrey.mcgill
11630         return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11631     },
11632
11633     /**
11634      * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11635      * automatically adjusting for Daylight Saving Time (DST) where applicable.
11636      * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11637      * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11638      * @return {Date} this or the clone.
11639      */
11640     clearTime : function(clone) {
11641         if (clone) {
11642             return this.clone().clearTime();
11643         }
11644
11645         // get current date before clearing time
11646         var d = this.getDate();
11647
11648         // clear time
11649         this.setHours(0);
11650         this.setMinutes(0);
11651         this.setSeconds(0);
11652         this.setMilliseconds(0);
11653
11654         if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11655             // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11656             // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11657
11658             // increment hour until cloned date == current date
11659             for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11660
11661             this.setDate(d);
11662             this.setHours(c.getHours());
11663         }
11664
11665         return this;
11666     },
11667
11668     /**
11669      * Provides a convenient method for performing basic date arithmetic. This method
11670      * does not modify the Date instance being called - it creates and returns
11671      * a new Date instance containing the resulting date value.
11672      *
11673      * Examples:
11674      * <pre><code>
11675 // Basic usage:
11676 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11677 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11678
11679 // Negative values will be subtracted:
11680 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11681 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11682
11683 // You can even chain several calls together in one line:
11684 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11685 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11686 </code></pre>
11687      *
11688      * @param {String} interval A valid date interval enum value.
11689      * @param {Number} value The amount to add to the current date.
11690      * @return {Date} The new Date instance.
11691      */
11692     add : function(interval, value) {
11693         var d = this.clone();
11694         if (!interval || value === 0) return d;
11695
11696         switch(interval.toLowerCase()) {
11697             case Date.MILLI:
11698                 d.setMilliseconds(this.getMilliseconds() + value);
11699                 break;
11700             case Date.SECOND:
11701                 d.setSeconds(this.getSeconds() + value);
11702                 break;
11703             case Date.MINUTE:
11704                 d.setMinutes(this.getMinutes() + value);
11705                 break;
11706             case Date.HOUR:
11707                 d.setHours(this.getHours() + value);
11708                 break;
11709             case Date.DAY:
11710                 d.setDate(this.getDate() + value);
11711                 break;
11712             case Date.MONTH:
11713                 var day = this.getDate();
11714                 if (day > 28) {
11715                     day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11716                 }
11717                 d.setDate(day);
11718                 d.setMonth(this.getMonth() + value);
11719                 break;
11720             case Date.YEAR:
11721                 d.setFullYear(this.getFullYear() + value);
11722                 break;
11723         }
11724         return d;
11725     },
11726
11727     /**
11728      * Checks if this date falls on or between the given start and end dates.
11729      * @param {Date} start Start date
11730      * @param {Date} end End date
11731      * @return {Boolean} true if this date falls on or between the given start and end dates.
11732      */
11733     between : function(start, end) {
11734         var t = this.getTime();
11735         return start.getTime() <= t && t <= end.getTime();
11736     }
11737 });
11738
11739
11740 /**
11741  * Formats a date given the supplied format string.
11742  * @param {String} format The format string.
11743  * @return {String} The formatted date.
11744  * @method format
11745  */
11746 Date.prototype.format = Date.prototype.dateFormat;
11747
11748
11749 // private
11750 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11751     Ext.apply(Date.prototype, {
11752         _xMonth : Date.prototype.setMonth,
11753         _xDate  : Date.prototype.setDate,
11754
11755         // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11756         // Date.setMonth does not work consistently if iMonth is not 0-11
11757         setMonth : function(num) {
11758             if (num <= -1) {
11759                 var n = Math.ceil(-num),
11760                     back_year = Math.ceil(n / 12),
11761                     month = (n % 12) ? 12 - n % 12 : 0;
11762
11763                 this.setFullYear(this.getFullYear() - back_year);
11764
11765                 return this._xMonth(month);
11766             } else {
11767                 return this._xMonth(num);
11768             }
11769         },
11770
11771         // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11772         // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11773         // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11774         setDate : function(d) {
11775             // use setTime() to workaround setDate() bug
11776             // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11777             return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11778         }
11779     });
11780 }
11781
11782
11783
11784 /* Some basic Date tests... (requires Firebug)
11785
11786 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11787 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11788
11789 // standard tests
11790 console.group('Standard Date.parseDate() Tests');
11791     console.log('Date.parseDate("2009-01-05T11:38:56", "c")               = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11792     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
11793     console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c")       = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11794     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
11795     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
11796 console.groupEnd();
11797
11798 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11799 // -- accepts ALL 6 levels of date-time granularity
11800 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11801     console.log('Date.parseDate("1997", "c")                              = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11802     console.log('Date.parseDate("1997-07", "c")                           = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11803     console.log('Date.parseDate("1997-07-16", "c")                        = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11804     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)
11805     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)
11806     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)
11807     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)
11808     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
11809 console.groupEnd();
11810
11811 //*/
11812 /**
11813  * @class Ext.util.MixedCollection
11814  * @extends Ext.util.Observable
11815  * A Collection class that maintains both numeric indexes and keys and exposes events.
11816  * @constructor
11817  * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11818  * function should add function references to the collection. Defaults to
11819  * <tt>false</tt>.
11820  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11821  * and return the key value for that item.  This is used when available to look up the key on items that
11822  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
11823  * equivalent to providing an implementation for the {@link #getKey} method.
11824  */
11825 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11826     this.items = [];
11827     this.map = {};
11828     this.keys = [];
11829     this.length = 0;
11830     this.addEvents(
11831         /**
11832          * @event clear
11833          * Fires when the collection is cleared.
11834          */
11835         'clear',
11836         /**
11837          * @event add
11838          * Fires when an item is added to the collection.
11839          * @param {Number} index The index at which the item was added.
11840          * @param {Object} o The item added.
11841          * @param {String} key The key associated with the added item.
11842          */
11843         'add',
11844         /**
11845          * @event replace
11846          * Fires when an item is replaced in the collection.
11847          * @param {String} key he key associated with the new added.
11848          * @param {Object} old The item being replaced.
11849          * @param {Object} new The new item.
11850          */
11851         'replace',
11852         /**
11853          * @event remove
11854          * Fires when an item is removed from the collection.
11855          * @param {Object} o The item being removed.
11856          * @param {String} key (optional) The key associated with the removed item.
11857          */
11858         'remove',
11859         'sort'
11860     );
11861     this.allowFunctions = allowFunctions === true;
11862     if(keyFn){
11863         this.getKey = keyFn;
11864     }
11865     Ext.util.MixedCollection.superclass.constructor.call(this);
11866 };
11867
11868 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11869
11870     /**
11871      * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11872      * function should add function references to the collection. Defaults to
11873      * <tt>false</tt>.
11874      */
11875     allowFunctions : false,
11876
11877     /**
11878      * Adds an item to the collection. Fires the {@link #add} event when complete.
11879      * @param {String} key <p>The key to associate with the item, or the new item.</p>
11880      * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11881      * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11882      * the MixedCollection will be able to <i>derive</i> the key for the new item.
11883      * In this case just pass the new item in this parameter.</p>
11884      * @param {Object} o The item to add.
11885      * @return {Object} The item added.
11886      */
11887     add : function(key, o){
11888         if(arguments.length == 1){
11889             o = arguments[0];
11890             key = this.getKey(o);
11891         }
11892         if(typeof key != 'undefined' && key !== null){
11893             var old = this.map[key];
11894             if(typeof old != 'undefined'){
11895                 return this.replace(key, o);
11896             }
11897             this.map[key] = o;
11898         }
11899         this.length++;
11900         this.items.push(o);
11901         this.keys.push(key);
11902         this.fireEvent('add', this.length-1, o, key);
11903         return o;
11904     },
11905
11906     /**
11907       * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
11908       * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11909       * to return a different value as in the following examples:<pre><code>
11910 // normal way
11911 var mc = new Ext.util.MixedCollection();
11912 mc.add(someEl.dom.id, someEl);
11913 mc.add(otherEl.dom.id, otherEl);
11914 //and so on
11915
11916 // using getKey
11917 var mc = new Ext.util.MixedCollection();
11918 mc.getKey = function(el){
11919    return el.dom.id;
11920 };
11921 mc.add(someEl);
11922 mc.add(otherEl);
11923
11924 // or via the constructor
11925 var mc = new Ext.util.MixedCollection(false, function(el){
11926    return el.dom.id;
11927 });
11928 mc.add(someEl);
11929 mc.add(otherEl);
11930      * </code></pre>
11931      * @param {Object} item The item for which to find the key.
11932      * @return {Object} The key for the passed item.
11933      */
11934     getKey : function(o){
11935          return o.id;
11936     },
11937
11938     /**
11939      * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11940      * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11941      * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11942      * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11943      * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11944      * with one having the same key value, then just pass the replacement item in this parameter.</p>
11945      * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11946      * with that key.
11947      * @return {Object}  The new item.
11948      */
11949     replace : function(key, o){
11950         if(arguments.length == 1){
11951             o = arguments[0];
11952             key = this.getKey(o);
11953         }
11954         var old = this.map[key];
11955         if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11956              return this.add(key, o);
11957         }
11958         var index = this.indexOfKey(key);
11959         this.items[index] = o;
11960         this.map[key] = o;
11961         this.fireEvent('replace', key, old, o);
11962         return o;
11963     },
11964
11965     /**
11966      * Adds all elements of an Array or an Object to the collection.
11967      * @param {Object/Array} objs An Object containing properties which will be added
11968      * to the collection, or an Array of values, each of which are added to the collection.
11969      * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11970      * has been set to <tt>true</tt>.
11971      */
11972     addAll : function(objs){
11973         if(arguments.length > 1 || Ext.isArray(objs)){
11974             var args = arguments.length > 1 ? arguments : objs;
11975             for(var i = 0, len = args.length; i < len; i++){
11976                 this.add(args[i]);
11977             }
11978         }else{
11979             for(var key in objs){
11980                 if(this.allowFunctions || typeof objs[key] != 'function'){
11981                     this.add(key, objs[key]);
11982                 }
11983             }
11984         }
11985     },
11986
11987     /**
11988      * Executes the specified function once for every item in the collection, passing the following arguments:
11989      * <div class="mdetail-params"><ul>
11990      * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11991      * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11992      * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11993      * </ul></div>
11994      * The function should return a boolean value. Returning false from the function will stop the iteration.
11995      * @param {Function} fn The function to execute for each item.
11996      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11997      */
11998     each : function(fn, scope){
11999         var items = [].concat(this.items); // each safe for removal
12000         for(var i = 0, len = items.length; i < len; i++){
12001             if(fn.call(scope || items[i], items[i], i, len) === false){
12002                 break;
12003             }
12004         }
12005     },
12006
12007     /**
12008      * Executes the specified function once for every key in the collection, passing each
12009      * key, and its associated item as the first two parameters.
12010      * @param {Function} fn The function to execute for each item.
12011      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12012      */
12013     eachKey : function(fn, scope){
12014         for(var i = 0, len = this.keys.length; i < len; i++){
12015             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12016         }
12017     },
12018
12019     /**
12020      * Returns the first item in the collection which elicits a true return value from the
12021      * passed selection function.
12022      * @param {Function} fn The selection function to execute for each item.
12023      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12024      * @return {Object} The first item in the collection which returned true from the selection function.
12025      */
12026     find : function(fn, scope){
12027         for(var i = 0, len = this.items.length; i < len; i++){
12028             if(fn.call(scope || window, this.items[i], this.keys[i])){
12029                 return this.items[i];
12030             }
12031         }
12032         return null;
12033     },
12034
12035     /**
12036      * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
12037      * @param {Number} index The index to insert the item at.
12038      * @param {String} key The key to associate with the new item, or the item itself.
12039      * @param {Object} o (optional) If the second parameter was a key, the new item.
12040      * @return {Object} The item inserted.
12041      */
12042     insert : function(index, key, o){
12043         if(arguments.length == 2){
12044             o = arguments[1];
12045             key = this.getKey(o);
12046         }
12047         if(this.containsKey(key)){
12048             this.suspendEvents();
12049             this.removeKey(key);
12050             this.resumeEvents();
12051         }
12052         if(index >= this.length){
12053             return this.add(key, o);
12054         }
12055         this.length++;
12056         this.items.splice(index, 0, o);
12057         if(typeof key != 'undefined' && key !== null){
12058             this.map[key] = o;
12059         }
12060         this.keys.splice(index, 0, key);
12061         this.fireEvent('add', index, o, key);
12062         return o;
12063     },
12064
12065     /**
12066      * Remove an item from the collection.
12067      * @param {Object} o The item to remove.
12068      * @return {Object} The item removed or false if no item was removed.
12069      */
12070     remove : function(o){
12071         return this.removeAt(this.indexOf(o));
12072     },
12073
12074     /**
12075      * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12076      * @param {Number} index The index within the collection of the item to remove.
12077      * @return {Object} The item removed or false if no item was removed.
12078      */
12079     removeAt : function(index){
12080         if(index < this.length && index >= 0){
12081             this.length--;
12082             var o = this.items[index];
12083             this.items.splice(index, 1);
12084             var key = this.keys[index];
12085             if(typeof key != 'undefined'){
12086                 delete this.map[key];
12087             }
12088             this.keys.splice(index, 1);
12089             this.fireEvent('remove', o, key);
12090             return o;
12091         }
12092         return false;
12093     },
12094
12095     /**
12096      * Removed an item associated with the passed key fom the collection.
12097      * @param {String} key The key of the item to remove.
12098      * @return {Object} The item removed or false if no item was removed.
12099      */
12100     removeKey : function(key){
12101         return this.removeAt(this.indexOfKey(key));
12102     },
12103
12104     /**
12105      * Returns the number of items in the collection.
12106      * @return {Number} the number of items in the collection.
12107      */
12108     getCount : function(){
12109         return this.length;
12110     },
12111
12112     /**
12113      * Returns index within the collection of the passed Object.
12114      * @param {Object} o The item to find the index of.
12115      * @return {Number} index of the item. Returns -1 if not found.
12116      */
12117     indexOf : function(o){
12118         return this.items.indexOf(o);
12119     },
12120
12121     /**
12122      * Returns index within the collection of the passed key.
12123      * @param {String} key The key to find the index of.
12124      * @return {Number} index of the key.
12125      */
12126     indexOfKey : function(key){
12127         return this.keys.indexOf(key);
12128     },
12129
12130     /**
12131      * Returns the item associated with the passed key OR index.
12132      * Key has priority over index.  This is the equivalent
12133      * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12134      * @param {String/Number} key The key or index of the item.
12135      * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
12136      * If an item was found, but is a Class, returns <tt>null</tt>.
12137      */
12138     item : function(key){
12139         var mk = this.map[key],
12140             item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12141         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12142     },
12143
12144     /**
12145      * Returns the item at the specified index.
12146      * @param {Number} index The index of the item.
12147      * @return {Object} The item at the specified index.
12148      */
12149     itemAt : function(index){
12150         return this.items[index];
12151     },
12152
12153     /**
12154      * Returns the item associated with the passed key.
12155      * @param {String/Number} key The key of the item.
12156      * @return {Object} The item associated with the passed key.
12157      */
12158     key : function(key){
12159         return this.map[key];
12160     },
12161
12162     /**
12163      * Returns true if the collection contains the passed Object as an item.
12164      * @param {Object} o  The Object to look for in the collection.
12165      * @return {Boolean} True if the collection contains the Object as an item.
12166      */
12167     contains : function(o){
12168         return this.indexOf(o) != -1;
12169     },
12170
12171     /**
12172      * Returns true if the collection contains the passed Object as a key.
12173      * @param {String} key The key to look for in the collection.
12174      * @return {Boolean} True if the collection contains the Object as a key.
12175      */
12176     containsKey : function(key){
12177         return typeof this.map[key] != 'undefined';
12178     },
12179
12180     /**
12181      * Removes all items from the collection.  Fires the {@link #clear} event when complete.
12182      */
12183     clear : function(){
12184         this.length = 0;
12185         this.items = [];
12186         this.keys = [];
12187         this.map = {};
12188         this.fireEvent('clear');
12189     },
12190
12191     /**
12192      * Returns the first item in the collection.
12193      * @return {Object} the first item in the collection..
12194      */
12195     first : function(){
12196         return this.items[0];
12197     },
12198
12199     /**
12200      * Returns the last item in the collection.
12201      * @return {Object} the last item in the collection..
12202      */
12203     last : function(){
12204         return this.items[this.length-1];
12205     },
12206
12207     /**
12208      * @private
12209      * Performs the actual sorting based on a direction and a sorting function. Internally,
12210      * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12211      * the sorted array data back into this.items and this.keys
12212      * @param {String} property Property to sort by ('key', 'value', or 'index')
12213      * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12214      * @param {Function} fn (optional) Comparison function that defines the sort order.
12215      * Defaults to sorting by numeric value.
12216      */
12217     _sort : function(property, dir, fn){
12218         var i, len,
12219             dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12220
12221             //this is a temporary array used to apply the sorting function
12222             c     = [],
12223             keys  = this.keys,
12224             items = this.items;
12225
12226         //default to a simple sorter function if one is not provided
12227         fn = fn || function(a, b) {
12228             return a - b;
12229         };
12230
12231         //copy all the items into a temporary array, which we will sort
12232         for(i = 0, len = items.length; i < len; i++){
12233             c[c.length] = {
12234                 key  : keys[i],
12235                 value: items[i],
12236                 index: i
12237             };
12238         }
12239
12240         //sort the temporary array
12241         c.sort(function(a, b){
12242             var v = fn(a[property], b[property]) * dsc;
12243             if(v === 0){
12244                 v = (a.index < b.index ? -1 : 1);
12245             }
12246             return v;
12247         });
12248
12249         //copy the temporary array back into the main this.items and this.keys objects
12250         for(i = 0, len = c.length; i < len; i++){
12251             items[i] = c[i].value;
12252             keys[i]  = c[i].key;
12253         }
12254
12255         this.fireEvent('sort', this);
12256     },
12257
12258     /**
12259      * Sorts this collection by <b>item</b> value with the passed comparison function.
12260      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12261      * @param {Function} fn (optional) Comparison function that defines the sort order.
12262      * Defaults to sorting by numeric value.
12263      */
12264     sort : function(dir, fn){
12265         this._sort('value', dir, fn);
12266     },
12267
12268     /**
12269      * Reorders each of the items based on a mapping from old index to new index. Internally this
12270      * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12271      * @param {Object} mapping Mapping from old item index to new item index
12272      */
12273     reorder: function(mapping) {
12274         this.suspendEvents();
12275
12276         var items     = this.items,
12277             index     = 0,
12278             length    = items.length,
12279             order     = [],
12280             remaining = [];
12281
12282         //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12283         for (oldIndex in mapping) {
12284             order[mapping[oldIndex]] = items[oldIndex];
12285         }
12286
12287         for (index = 0; index < length; index++) {
12288             if (mapping[index] == undefined) {
12289                 remaining.push(items[index]);
12290             }
12291         }
12292
12293         for (index = 0; index < length; index++) {
12294             if (order[index] == undefined) {
12295                 order[index] = remaining.shift();
12296             }
12297         }
12298
12299         this.clear();
12300         this.addAll(order);
12301
12302         this.resumeEvents();
12303         this.fireEvent('sort', this);
12304     },
12305
12306     /**
12307      * Sorts this collection by <b>key</b>s.
12308      * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12309      * @param {Function} fn (optional) Comparison function that defines the sort order.
12310      * Defaults to sorting by case insensitive string.
12311      */
12312     keySort : function(dir, fn){
12313         this._sort('key', dir, fn || function(a, b){
12314             var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12315             return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12316         });
12317     },
12318
12319     /**
12320      * Returns a range of items in this collection
12321      * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12322      * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12323      * @return {Array} An array of items
12324      */
12325     getRange : function(start, end){
12326         var items = this.items;
12327         if(items.length < 1){
12328             return [];
12329         }
12330         start = start || 0;
12331         end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12332         var i, r = [];
12333         if(start <= end){
12334             for(i = start; i <= end; i++) {
12335                 r[r.length] = items[i];
12336             }
12337         }else{
12338             for(i = start; i >= end; i--) {
12339                 r[r.length] = items[i];
12340             }
12341         }
12342         return r;
12343     },
12344
12345     /**
12346      * Filter the <i>objects</i> in this collection by a specific property.
12347      * Returns a new collection that has been filtered.
12348      * @param {String} property A property on your objects
12349      * @param {String/RegExp} value Either string that the property values
12350      * should start with or a RegExp to test against the property
12351      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12352      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12353      * @return {MixedCollection} The new filtered collection
12354      */
12355     filter : function(property, value, anyMatch, caseSensitive){
12356         if(Ext.isEmpty(value, false)){
12357             return this.clone();
12358         }
12359         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12360         return this.filterBy(function(o){
12361             return o && value.test(o[property]);
12362         });
12363     },
12364
12365     /**
12366      * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12367      * The passed function will be called with each object in the collection.
12368      * If the function returns true, the value is included otherwise it is filtered.
12369      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12370      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12371      * @return {MixedCollection} The new filtered collection
12372      */
12373     filterBy : function(fn, scope){
12374         var r = new Ext.util.MixedCollection();
12375         r.getKey = this.getKey;
12376         var k = this.keys, it = this.items;
12377         for(var i = 0, len = it.length; i < len; i++){
12378             if(fn.call(scope||this, it[i], k[i])){
12379                 r.add(k[i], it[i]);
12380             }
12381         }
12382         return r;
12383     },
12384
12385     /**
12386      * Finds the index of the first matching object in this collection by a specific property/value.
12387      * @param {String} property The name of a property on your objects.
12388      * @param {String/RegExp} value A string that the property values
12389      * should start with or a RegExp to test against the property.
12390      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12391      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12392      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12393      * @return {Number} The matched index or -1
12394      */
12395     findIndex : function(property, value, start, anyMatch, caseSensitive){
12396         if(Ext.isEmpty(value, false)){
12397             return -1;
12398         }
12399         value = this.createValueMatcher(value, anyMatch, caseSensitive);
12400         return this.findIndexBy(function(o){
12401             return o && value.test(o[property]);
12402         }, null, start);
12403     },
12404
12405     /**
12406      * Find the index of the first matching object in this collection by a function.
12407      * If the function returns <i>true</i> it is considered a match.
12408      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12409      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12410      * @param {Number} start (optional) The index to start searching at (defaults to 0).
12411      * @return {Number} The matched index or -1
12412      */
12413     findIndexBy : function(fn, scope, start){
12414         var k = this.keys, it = this.items;
12415         for(var i = (start||0), len = it.length; i < len; i++){
12416             if(fn.call(scope||this, it[i], k[i])){
12417                 return i;
12418             }
12419         }
12420         return -1;
12421     },
12422
12423     /**
12424      * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12425      * and by Ext.data.Store#filter
12426      * @private
12427      * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12428      * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12429      * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12430      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12431      */
12432     createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12433         if (!value.exec) { // not a regex
12434             var er = Ext.escapeRe;
12435             value = String(value);
12436
12437             if (anyMatch === true) {
12438                 value = er(value);
12439             } else {
12440                 value = '^' + er(value);
12441                 if (exactMatch === true) {
12442                     value += '$';
12443                 }
12444             }
12445             value = new RegExp(value, caseSensitive ? '' : 'i');
12446          }
12447          return value;
12448     },
12449
12450     /**
12451      * Creates a shallow copy of this collection
12452      * @return {MixedCollection}
12453      */
12454     clone : function(){
12455         var r = new Ext.util.MixedCollection();
12456         var k = this.keys, it = this.items;
12457         for(var i = 0, len = it.length; i < len; i++){
12458             r.add(k[i], it[i]);
12459         }
12460         r.getKey = this.getKey;
12461         return r;
12462     }
12463 });
12464 /**
12465  * This method calls {@link #item item()}.
12466  * Returns the item associated with the passed key OR index. Key has priority
12467  * over index.  This is the equivalent of calling {@link #key} first, then if
12468  * nothing matched calling {@link #itemAt}.
12469  * @param {String/Number} key The key or index of the item.
12470  * @return {Object} If the item is found, returns the item.  If the item was
12471  * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12472  * returns <tt>null</tt>.
12473  */
12474 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
12475 /**
12476  * @class Ext.util.JSON
12477  * Modified version of Douglas Crockford"s json.js that doesn"t
12478  * mess with the Object prototype
12479  * http://www.json.org/js.html
12480  * @singleton
12481  */
12482 Ext.util.JSON = new (function(){
12483     var useHasOwn = !!{}.hasOwnProperty,
12484         isNative = function() {
12485             var useNative = null;
12486
12487             return function() {
12488                 if (useNative === null) {
12489                     useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12490                 }
12491         
12492                 return useNative;
12493             };
12494         }(),
12495         pad = function(n) {
12496             return n < 10 ? "0" + n : n;
12497         },
12498         doDecode = function(json){
12499             return eval("(" + json + ')');    
12500         },
12501         doEncode = function(o){
12502             if(!Ext.isDefined(o) || o === null){
12503                 return "null";
12504             }else if(Ext.isArray(o)){
12505                 return encodeArray(o);
12506             }else if(Ext.isDate(o)){
12507                 return Ext.util.JSON.encodeDate(o);
12508             }else if(Ext.isString(o)){
12509                 return encodeString(o);
12510             }else if(typeof o == "number"){
12511                 //don't use isNumber here, since finite checks happen inside isNumber
12512                 return isFinite(o) ? String(o) : "null";
12513             }else if(Ext.isBoolean(o)){
12514                 return String(o);
12515             }else {
12516                 var a = ["{"], b, i, v;
12517                 for (i in o) {
12518                     // don't encode DOM objects
12519                     if(!o.getElementsByTagName){
12520                         if(!useHasOwn || o.hasOwnProperty(i)) {
12521                             v = o[i];
12522                             switch (typeof v) {
12523                             case "undefined":
12524                             case "function":
12525                             case "unknown":
12526                                 break;
12527                             default:
12528                                 if(b){
12529                                     a.push(',');
12530                                 }
12531                                 a.push(doEncode(i), ":",
12532                                         v === null ? "null" : doEncode(v));
12533                                 b = true;
12534                             }
12535                         }
12536                     }
12537                 }
12538                 a.push("}");
12539                 return a.join("");
12540             }    
12541         },
12542         m = {
12543             "\b": '\\b',
12544             "\t": '\\t',
12545             "\n": '\\n',
12546             "\f": '\\f',
12547             "\r": '\\r',
12548             '"' : '\\"',
12549             "\\": '\\\\'
12550         },
12551         encodeString = function(s){
12552             if (/["\\\x00-\x1f]/.test(s)) {
12553                 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12554                     var c = m[b];
12555                     if(c){
12556                         return c;
12557                     }
12558                     c = b.charCodeAt();
12559                     return "\\u00" +
12560                         Math.floor(c / 16).toString(16) +
12561                         (c % 16).toString(16);
12562                 }) + '"';
12563             }
12564             return '"' + s + '"';
12565         },
12566         encodeArray = function(o){
12567             var a = ["["], b, i, l = o.length, v;
12568                 for (i = 0; i < l; i += 1) {
12569                     v = o[i];
12570                     switch (typeof v) {
12571                         case "undefined":
12572                         case "function":
12573                         case "unknown":
12574                             break;
12575                         default:
12576                             if (b) {
12577                                 a.push(',');
12578                             }
12579                             a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12580                             b = true;
12581                     }
12582                 }
12583                 a.push("]");
12584                 return a.join("");
12585         };
12586
12587     /**
12588      * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12589      * <b>The returned value includes enclosing double quotation marks.</b></p>
12590      * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12591      * <p>To override this:</p><pre><code>
12592 Ext.util.JSON.encodeDate = function(d) {
12593     return d.format('"Y-m-d"');
12594 };
12595 </code></pre>
12596      * @param {Date} d The Date to encode
12597      * @return {String} The string literal to use in a JSON string.
12598      */
12599     this.encodeDate = function(o){
12600         return '"' + o.getFullYear() + "-" +
12601                 pad(o.getMonth() + 1) + "-" +
12602                 pad(o.getDate()) + "T" +
12603                 pad(o.getHours()) + ":" +
12604                 pad(o.getMinutes()) + ":" +
12605                 pad(o.getSeconds()) + '"';
12606     };
12607
12608     /**
12609      * Encodes an Object, Array or other value
12610      * @param {Mixed} o The variable to encode
12611      * @return {String} The JSON string
12612      */
12613     this.encode = function() {
12614         var ec;
12615         return function(o) {
12616             if (!ec) {
12617                 // setup encoding function on first access
12618                 ec = isNative() ? JSON.stringify : doEncode;
12619             }
12620             return ec(o);
12621         };
12622     }();
12623
12624
12625     /**
12626      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12627      * @param {String} json The JSON string
12628      * @return {Object} The resulting object
12629      */
12630     this.decode = function() {
12631         var dc;
12632         return function(json) {
12633             if (!dc) {
12634                 // setup decoding function on first access
12635                 dc = isNative() ? JSON.parse : doDecode;
12636             }
12637             return dc(json);
12638         };
12639     }();
12640
12641 })();
12642 /**
12643  * Shorthand for {@link Ext.util.JSON#encode}
12644  * @param {Mixed} o The variable to encode
12645  * @return {String} The JSON string
12646  * @member Ext
12647  * @method encode
12648  */
12649 Ext.encode = Ext.util.JSON.encode;
12650 /**
12651  * Shorthand for {@link Ext.util.JSON#decode}
12652  * @param {String} json The JSON string
12653  * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12654  * @return {Object} The resulting object
12655  * @member Ext
12656  * @method decode
12657  */
12658 Ext.decode = Ext.util.JSON.decode;
12659 /**
12660  * @class Ext.util.Format
12661  * Reusable data formatting functions
12662  * @singleton
12663  */
12664 Ext.util.Format = function(){
12665     var trimRe = /^\s+|\s+$/g,
12666         stripTagsRE = /<\/?[^>]+>/gi,
12667         stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12668         nl2brRe = /\r?\n/g;
12669
12670     return {
12671         /**
12672          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12673          * @param {String} value The string to truncate
12674          * @param {Number} length The maximum length to allow before truncating
12675          * @param {Boolean} word True to try to find a common work break
12676          * @return {String} The converted text
12677          */
12678         ellipsis : function(value, len, word){
12679             if(value && value.length > len){
12680                 if(word){
12681                     var vs = value.substr(0, len - 2),
12682                         index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12683                     if(index == -1 || index < (len - 15)){
12684                         return value.substr(0, len - 3) + "...";
12685                     }else{
12686                         return vs.substr(0, index) + "...";
12687                     }
12688                 } else{
12689                     return value.substr(0, len - 3) + "...";
12690                 }
12691             }
12692             return value;
12693         },
12694
12695         /**
12696          * Checks a reference and converts it to empty string if it is undefined
12697          * @param {Mixed} value Reference to check
12698          * @return {Mixed} Empty string if converted, otherwise the original value
12699          */
12700         undef : function(value){
12701             return value !== undefined ? value : "";
12702         },
12703
12704         /**
12705          * Checks a reference and converts it to the default value if it's empty
12706          * @param {Mixed} value Reference to check
12707          * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12708          * @return {String}
12709          */
12710         defaultValue : function(value, defaultValue){
12711             return value !== undefined && value !== '' ? value : defaultValue;
12712         },
12713
12714         /**
12715          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12716          * @param {String} value The string to encode
12717          * @return {String} The encoded text
12718          */
12719         htmlEncode : function(value){
12720             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12721         },
12722
12723         /**
12724          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12725          * @param {String} value The string to decode
12726          * @return {String} The decoded text
12727          */
12728         htmlDecode : function(value){
12729             return !value ? value : String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
12730         },
12731
12732         /**
12733          * Trims any whitespace from either side of a string
12734          * @param {String} value The text to trim
12735          * @return {String} The trimmed text
12736          */
12737         trim : function(value){
12738             return String(value).replace(trimRe, "");
12739         },
12740
12741         /**
12742          * Returns a substring from within an original string
12743          * @param {String} value The original text
12744          * @param {Number} start The start index of the substring
12745          * @param {Number} length The length of the substring
12746          * @return {String} The substring
12747          */
12748         substr : function(value, start, length){
12749             return String(value).substr(start, length);
12750         },
12751
12752         /**
12753          * Converts a string to all lower case letters
12754          * @param {String} value The text to convert
12755          * @return {String} The converted text
12756          */
12757         lowercase : function(value){
12758             return String(value).toLowerCase();
12759         },
12760
12761         /**
12762          * Converts a string to all upper case letters
12763          * @param {String} value The text to convert
12764          * @return {String} The converted text
12765          */
12766         uppercase : function(value){
12767             return String(value).toUpperCase();
12768         },
12769
12770         /**
12771          * Converts the first character only of a string to upper case
12772          * @param {String} value The text to convert
12773          * @return {String} The converted text
12774          */
12775         capitalize : function(value){
12776             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12777         },
12778
12779         // private
12780         call : function(value, fn){
12781             if(arguments.length > 2){
12782                 var args = Array.prototype.slice.call(arguments, 2);
12783                 args.unshift(value);
12784                 return eval(fn).apply(window, args);
12785             }else{
12786                 return eval(fn).call(window, value);
12787             }
12788         },
12789
12790         /**
12791          * Format a number as US currency
12792          * @param {Number/String} value The numeric value to format
12793          * @return {String} The formatted currency string
12794          */
12795         usMoney : function(v){
12796             v = (Math.round((v-0)*100))/100;
12797             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12798             v = String(v);
12799             var ps = v.split('.'),
12800                 whole = ps[0],
12801                 sub = ps[1] ? '.'+ ps[1] : '.00',
12802                 r = /(\d+)(\d{3})/;
12803             while (r.test(whole)) {
12804                 whole = whole.replace(r, '$1' + ',' + '$2');
12805             }
12806             v = whole + sub;
12807             if(v.charAt(0) == '-'){
12808                 return '-$' + v.substr(1);
12809             }
12810             return "$" +  v;
12811         },
12812
12813         /**
12814          * Parse a value into a formatted date using the specified format pattern.
12815          * @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)
12816          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12817          * @return {String} The formatted date string
12818          */
12819         date : function(v, format){
12820             if(!v){
12821                 return "";
12822             }
12823             if(!Ext.isDate(v)){
12824                 v = new Date(Date.parse(v));
12825             }
12826             return v.dateFormat(format || "m/d/Y");
12827         },
12828
12829         /**
12830          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12831          * @param {String} format Any valid date format string
12832          * @return {Function} The date formatting function
12833          */
12834         dateRenderer : function(format){
12835             return function(v){
12836                 return Ext.util.Format.date(v, format);
12837             };
12838         },
12839
12840         /**
12841          * Strips all HTML tags
12842          * @param {Mixed} value The text from which to strip tags
12843          * @return {String} The stripped text
12844          */
12845         stripTags : function(v){
12846             return !v ? v : String(v).replace(stripTagsRE, "");
12847         },
12848
12849         /**
12850          * Strips all script tags
12851          * @param {Mixed} value The text from which to strip script tags
12852          * @return {String} The stripped text
12853          */
12854         stripScripts : function(v){
12855             return !v ? v : String(v).replace(stripScriptsRe, "");
12856         },
12857
12858         /**
12859          * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12860          * @param {Number/String} size The numeric value to format
12861          * @return {String} The formatted file size
12862          */
12863         fileSize : function(size){
12864             if(size < 1024) {
12865                 return size + " bytes";
12866             } else if(size < 1048576) {
12867                 return (Math.round(((size*10) / 1024))/10) + " KB";
12868             } else {
12869                 return (Math.round(((size*10) / 1048576))/10) + " MB";
12870             }
12871         },
12872
12873         /**
12874          * It does simple math for use in a template, for example:<pre><code>
12875          * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12876          * </code></pre>
12877          * @return {Function} A function that operates on the passed value.
12878          */
12879         math : function(){
12880             var fns = {};
12881             return function(v, a){
12882                 if(!fns[a]){
12883                     fns[a] = new Function('v', 'return v ' + a + ';');
12884                 }
12885                 return fns[a](v);
12886             }
12887         }(),
12888
12889         /**
12890          * Rounds the passed number to the required decimal precision.
12891          * @param {Number/String} value The numeric value to round.
12892          * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12893          * @return {Number} The rounded value.
12894          */
12895         round : function(value, precision) {
12896             var result = Number(value);
12897             if (typeof precision == 'number') {
12898                 precision = Math.pow(10, precision);
12899                 result = Math.round(value * precision) / precision;
12900             }
12901             return result;
12902         },
12903
12904         /**
12905          * Formats the number according to the format string.
12906          * <div style="margin-left:40px">examples (123456.789):
12907          * <div style="margin-left:10px">
12908          * 0 - (123456) show only digits, no precision<br>
12909          * 0.00 - (123456.78) show only digits, 2 precision<br>
12910          * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12911          * 0,000 - (123,456) show comma and digits, no precision<br>
12912          * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12913          * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12914          * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12915          * For example: 0.000,00/i
12916          * </div></div>
12917          * @param {Number} v The number to format.
12918          * @param {String} format The way you would like to format this text.
12919          * @return {String} The formatted number.
12920          */
12921         number: function(v, format) {
12922             if(!format){
12923                 return v;
12924             }
12925             v = Ext.num(v, NaN);
12926             if (isNaN(v)){
12927                 return '';
12928             }
12929             var comma = ',',
12930                 dec = '.',
12931                 i18n = false,
12932                 neg = v < 0;
12933
12934             v = Math.abs(v);
12935             if(format.substr(format.length - 2) == '/i'){
12936                 format = format.substr(0, format.length - 2);
12937                 i18n = true;
12938                 comma = '.';
12939                 dec = ',';
12940             }
12941
12942             var hasComma = format.indexOf(comma) != -1,
12943                 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12944
12945             if(1 < psplit.length){
12946                 v = v.toFixed(psplit[1].length);
12947             }else if(2 < psplit.length){
12948                 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12949             }else{
12950                 v = v.toFixed(0);
12951             }
12952
12953             var fnum = v.toString();
12954
12955             psplit = fnum.split('.');
12956
12957             if (hasComma) {
12958                 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12959
12960                 for (var i = 0; i < j; i += n) {
12961                     if (i != 0) {
12962                         n = 3;
12963                     }
12964                     parr[parr.length] = cnum.substr(i, n);
12965                     m -= 1;
12966                 }
12967                 fnum = parr.join(comma);
12968                 if (psplit[1]) {
12969                     fnum += dec + psplit[1];
12970                 }
12971             } else {
12972                 if (psplit[1]) {
12973                     fnum = psplit[0] + dec + psplit[1];
12974                 }
12975             }
12976
12977             return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12978         },
12979
12980         /**
12981          * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12982          * @param {String} format Any valid number format string for {@link #number}
12983          * @return {Function} The number formatting function
12984          */
12985         numberRenderer : function(format){
12986             return function(v){
12987                 return Ext.util.Format.number(v, format);
12988             };
12989         },
12990
12991         /**
12992          * Selectively do a plural form of a word based on a numeric value. For example, in a template,
12993          * {commentCount:plural("Comment")}  would result in "1 Comment" if commentCount was 1 or would be "x Comments"
12994          * if the value is 0 or greater than 1.
12995          * @param {Number} value The value to compare against
12996          * @param {String} singular The singular form of the word
12997          * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
12998          */
12999         plural : function(v, s, p){
13000             return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
13001         },
13002
13003         /**
13004          * Converts newline characters to the HTML tag &lt;br/>
13005          * @param {String} The string value to format.
13006          * @return {String} The string with embedded &lt;br/> tags in place of newlines.
13007          */
13008         nl2br : function(v){
13009             return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
13010         }
13011     }
13012 }();
13013 /**
13014  * @class Ext.XTemplate
13015  * @extends Ext.Template
13016  * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
13017  * <li>Autofilling arrays using templates and sub-templates</li>
13018  * <li>Conditional processing with basic comparison operators</li>
13019  * <li>Basic math function support</li>
13020  * <li>Execute arbitrary inline code with special built-in template variables</li>
13021  * <li>Custom member functions</li>
13022  * <li>Many special tags and built-in operators that aren't defined as part of
13023  * the API, but are supported in the templates that can be created</li>
13024  * </ul></div></p>
13025  * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
13026  * <li>{@link Ext.DataView}</li>
13027  * <li>{@link Ext.ListView}</li>
13028  * <li>{@link Ext.form.ComboBox}</li>
13029  * <li>{@link Ext.grid.TemplateColumn}</li>
13030  * <li>{@link Ext.grid.GroupingView}</li>
13031  * <li>{@link Ext.menu.Item}</li>
13032  * <li>{@link Ext.layout.MenuLayout}</li>
13033  * <li>{@link Ext.ColorPalette}</li>
13034  * </ul></div></p>
13035  *
13036  * <p>For example usage {@link #XTemplate see the constructor}.</p>
13037  *
13038  * @constructor
13039  * The {@link Ext.Template#Template Ext.Template constructor} describes
13040  * the acceptable parameters to pass to the constructor. The following
13041  * examples demonstrate all of the supported features.</p>
13042  *
13043  * <div class="mdetail-params"><ul>
13044  *
13045  * <li><b><u>Sample Data</u></b>
13046  * <div class="sub-desc">
13047  * <p>This is the data object used for reference in each code example:</p>
13048  * <pre><code>
13049 var data = {
13050     name: 'Jack Slocum',
13051     title: 'Lead Developer',
13052     company: 'Ext JS, LLC',
13053     email: 'jack@extjs.com',
13054     address: '4 Red Bulls Drive',
13055     city: 'Cleveland',
13056     state: 'Ohio',
13057     zip: '44102',
13058     drinks: ['Red Bull', 'Coffee', 'Water'],
13059     kids: [{
13060         name: 'Sara Grace',
13061         age:3
13062     },{
13063         name: 'Zachary',
13064         age:2
13065     },{
13066         name: 'John James',
13067         age:0
13068     }]
13069 };
13070  * </code></pre>
13071  * </div>
13072  * </li>
13073  *
13074  *
13075  * <li><b><u>Auto filling of arrays</u></b>
13076  * <div class="sub-desc">
13077  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13078  * to process the provided data object:
13079  * <ul>
13080  * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13081  * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13082  * array.</li>
13083  * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13084  * <li>While processing an array, the special variable <tt>{#}</tt>
13085  * will provide the current array index + 1 (starts at 1, not 0).</li>
13086  * </ul>
13087  * </p>
13088  * <pre><code>
13089 &lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
13090 &lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
13091 &lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
13092  * </code></pre>
13093  * Using the sample data above:
13094  * <pre><code>
13095 var tpl = new Ext.XTemplate(
13096     '&lt;p>Kids: ',
13097     '&lt;tpl <b>for</b>=".">',       // process the data.kids node
13098         '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
13099     '&lt;/tpl>&lt;/p>'
13100 );
13101 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13102  * </code></pre>
13103  * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13104  * to access specified members of the provided data object to populate the template:</p>
13105  * <pre><code>
13106 var tpl = new Ext.XTemplate(
13107     '&lt;p>Name: {name}&lt;/p>',
13108     '&lt;p>Title: {title}&lt;/p>',
13109     '&lt;p>Company: {company}&lt;/p>',
13110     '&lt;p>Kids: ',
13111     '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
13112         '&lt;p>{name}&lt;/p>',
13113     '&lt;/tpl>&lt;/p>'
13114 );
13115 tpl.overwrite(panel.body, data);  // pass the root node of the data object
13116  * </code></pre>
13117  * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13118  * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
13119  * will represent the value of the array at the current index:</p>
13120  * <pre><code>
13121 var tpl = new Ext.XTemplate(
13122     '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
13123     '&lt;tpl for="drinks">',
13124        '&lt;div> - {.}&lt;/div>',
13125     '&lt;/tpl>'
13126 );
13127 tpl.overwrite(panel.body, data);
13128  * </code></pre>
13129  * <p>When processing a sub-template, for example while looping through a child array,
13130  * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13131  * <pre><code>
13132 var tpl = new Ext.XTemplate(
13133     '&lt;p>Name: {name}&lt;/p>',
13134     '&lt;p>Kids: ',
13135     '&lt;tpl for="kids">',
13136         '&lt;tpl if="age > 1">',
13137             '&lt;p>{name}&lt;/p>',
13138             '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
13139         '&lt;/tpl>',
13140     '&lt;/tpl>&lt;/p>'
13141 );
13142 tpl.overwrite(panel.body, data);
13143  * </code></pre>
13144  * </div>
13145  * </li>
13146  *
13147  *
13148  * <li><b><u>Conditional processing with basic comparison operators</u></b>
13149  * <div class="sub-desc">
13150  * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13151  * to provide conditional checks for deciding whether or not to render specific
13152  * parts of the template. Notes:<div class="sub-desc"><ul>
13153  * <li>Double quotes must be encoded if used within the conditional</li>
13154  * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
13155  * <tt>if</tt> statements should be used.</li>
13156  * </ul></div>
13157  * <pre><code>
13158 &lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
13159 &lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
13160 &lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
13161 &lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
13162 &lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
13163 // no good:
13164 &lt;tpl if="name == "Jack"">Hello&lt;/tpl>
13165 // encode &#34; if it is part of the condition, e.g.
13166 &lt;tpl if="name == &#38;quot;Jack&#38;quot;">Hello&lt;/tpl>
13167  * </code></pre>
13168  * Using the sample data above:
13169  * <pre><code>
13170 var tpl = new Ext.XTemplate(
13171     '&lt;p>Name: {name}&lt;/p>',
13172     '&lt;p>Kids: ',
13173     '&lt;tpl for="kids">',
13174         '&lt;tpl if="age > 1">',
13175             '&lt;p>{name}&lt;/p>',
13176         '&lt;/tpl>',
13177     '&lt;/tpl>&lt;/p>'
13178 );
13179 tpl.overwrite(panel.body, data);
13180  * </code></pre>
13181  * </div>
13182  * </li>
13183  *
13184  *
13185  * <li><b><u>Basic math support</u></b>
13186  * <div class="sub-desc">
13187  * <p>The following basic math operators may be applied directly on numeric
13188  * data values:</p><pre>
13189  * + - * /
13190  * </pre>
13191  * For example:
13192  * <pre><code>
13193 var tpl = new Ext.XTemplate(
13194     '&lt;p>Name: {name}&lt;/p>',
13195     '&lt;p>Kids: ',
13196     '&lt;tpl for="kids">',
13197         '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
13198             '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
13199             '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
13200             '&lt;p>Dad: {parent.name}&lt;/p>',
13201         '&lt;/tpl>',
13202     '&lt;/tpl>&lt;/p>'
13203 );
13204 tpl.overwrite(panel.body, data);
13205 </code></pre>
13206  * </div>
13207  * </li>
13208  *
13209  *
13210  * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
13211  * <div class="sub-desc">
13212  * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13213  * in the scope of the template. There are some special variables available in that code:
13214  * <ul>
13215  * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13216  * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13217  * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13218  * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13219  * loop you are in (1-based).</li>
13220  * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13221  * of the array you are looping.</li>
13222  * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13223  * </ul>
13224  * This example demonstrates basic row striping using an inline code block and the
13225  * <tt>xindex</tt> variable:</p>
13226  * <pre><code>
13227 var tpl = new Ext.XTemplate(
13228     '&lt;p>Name: {name}&lt;/p>',
13229     '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
13230     '&lt;p>Kids: ',
13231     '&lt;tpl for="kids">',
13232        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13233         '{name}',
13234         '&lt;/div>',
13235     '&lt;/tpl>&lt;/p>'
13236 );
13237 tpl.overwrite(panel.body, data);
13238  * </code></pre>
13239  * </div>
13240  * </li>
13241  *
13242  * <li><b><u>Template member functions</u></b>
13243  * <div class="sub-desc">
13244  * <p>One or more member functions can be specified in a configuration
13245  * object passed into the XTemplate constructor for more complex processing:</p>
13246  * <pre><code>
13247 var tpl = new Ext.XTemplate(
13248     '&lt;p>Name: {name}&lt;/p>',
13249     '&lt;p>Kids: ',
13250     '&lt;tpl for="kids">',
13251         '&lt;tpl if="this.isGirl(name)">',
13252             '&lt;p>Girl: {name} - {age}&lt;/p>',
13253         '&lt;/tpl>',
13254         // use opposite if statement to simulate 'else' processing:
13255         '&lt;tpl if="this.isGirl(name) == false">',
13256             '&lt;p>Boy: {name} - {age}&lt;/p>',
13257         '&lt;/tpl>',
13258         '&lt;tpl if="this.isBaby(age)">',
13259             '&lt;p>{name} is a baby!&lt;/p>',
13260         '&lt;/tpl>',
13261     '&lt;/tpl>&lt;/p>',
13262     {
13263         // XTemplate configuration:
13264         compiled: true,
13265         disableFormats: true,
13266         // member functions:
13267         isGirl: function(name){
13268             return name == 'Sara Grace';
13269         },
13270         isBaby: function(age){
13271             return age < 1;
13272         }
13273     }
13274 );
13275 tpl.overwrite(panel.body, data);
13276  * </code></pre>
13277  * </div>
13278  * </li>
13279  *
13280  * </ul></div>
13281  *
13282  * @param {Mixed} config
13283  */
13284 Ext.XTemplate = function(){
13285     Ext.XTemplate.superclass.constructor.apply(this, arguments);
13286
13287     var me = this,
13288         s = me.html,
13289         re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13290         nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13291         ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13292         execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13293         m,
13294         id = 0,
13295         tpls = [],
13296         VALUES = 'values',
13297         PARENT = 'parent',
13298         XINDEX = 'xindex',
13299         XCOUNT = 'xcount',
13300         RETURN = 'return ',
13301         WITHVALUES = 'with(values){ ';
13302
13303     s = ['<tpl>', s, '</tpl>'].join('');
13304
13305     while((m = s.match(re))){
13306         var m2 = m[0].match(nameRe),
13307             m3 = m[0].match(ifRe),
13308             m4 = m[0].match(execRe),
13309             exp = null,
13310             fn = null,
13311             exec = null,
13312             name = m2 && m2[1] ? m2[1] : '';
13313
13314        if (m3) {
13315            exp = m3 && m3[1] ? m3[1] : null;
13316            if(exp){
13317                fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13318            }
13319        }
13320        if (m4) {
13321            exp = m4 && m4[1] ? m4[1] : null;
13322            if(exp){
13323                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13324            }
13325        }
13326        if(name){
13327            switch(name){
13328                case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13329                case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13330                default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13331            }
13332        }
13333        tpls.push({
13334             id: id,
13335             target: name,
13336             exec: exec,
13337             test: fn,
13338             body: m[1]||''
13339         });
13340        s = s.replace(m[0], '{xtpl'+ id + '}');
13341        ++id;
13342     }
13343     for(var i = tpls.length-1; i >= 0; --i){
13344         me.compileTpl(tpls[i]);
13345     }
13346     me.master = tpls[tpls.length-1];
13347     me.tpls = tpls;
13348 };
13349 Ext.extend(Ext.XTemplate, Ext.Template, {
13350     // private
13351     re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13352     // private
13353     codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13354
13355     // private
13356     applySubTemplate : function(id, values, parent, xindex, xcount){
13357         var me = this,
13358             len,
13359             t = me.tpls[id],
13360             vs,
13361             buf = [];
13362         if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13363             (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13364             return '';
13365         }
13366         vs = t.target ? t.target.call(me, values, parent) : values;
13367         len = vs.length;
13368         parent = t.target ? values : parent;
13369         if(t.target && Ext.isArray(vs)){
13370             for(var i = 0, len = vs.length; i < len; i++){
13371                 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
13372             }
13373             return buf.join('');
13374         }
13375         return t.compiled.call(me, vs, parent, xindex, xcount);
13376     },
13377
13378     // private
13379     compileTpl : function(tpl){
13380         var fm = Ext.util.Format,
13381             useF = this.disableFormats !== true,
13382             sep = Ext.isGecko ? "+" : ",",
13383             body;
13384
13385         function fn(m, name, format, args, math){
13386             if(name.substr(0, 4) == 'xtpl'){
13387                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13388             }
13389             var v;
13390             if(name === '.'){
13391                 v = 'values';
13392             }else if(name === '#'){
13393                 v = 'xindex';
13394             }else if(name.indexOf('.') != -1){
13395                 v = name;
13396             }else{
13397                 v = "values['" + name + "']";
13398             }
13399             if(math){
13400                 v = '(' + v + math + ')';
13401             }
13402             if (format && useF) {
13403                 args = args ? ',' + args : "";
13404                 if(format.substr(0, 5) != "this."){
13405                     format = "fm." + format + '(';
13406                 }else{
13407                     format = 'this.call("'+ format.substr(5) + '", ';
13408                     args = ", values";
13409                 }
13410             } else {
13411                 args= ''; format = "("+v+" === undefined ? '' : ";
13412             }
13413             return "'"+ sep + format + v + args + ")"+sep+"'";
13414         }
13415
13416         function codeFn(m, code){
13417             // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13418             return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13419         }
13420
13421         // branched to use + in gecko and [].join() in others
13422         if(Ext.isGecko){
13423             body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13424                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13425                     "';};";
13426         }else{
13427             body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13428             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13429             body.push("'].join('');};");
13430             body = body.join('');
13431         }
13432         eval(body);
13433         return this;
13434     },
13435
13436     /**
13437      * Returns an HTML fragment of this template with the specified values applied.
13438      * @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'})
13439      * @return {String} The HTML fragment
13440      */
13441     applyTemplate : function(values){
13442         return this.master.compiled.call(this, values, {}, 1, 1);
13443     },
13444
13445     /**
13446      * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
13447      * @return {Function} The compiled function
13448      */
13449     compile : function(){return this;}
13450
13451     /**
13452      * @property re
13453      * @hide
13454      */
13455     /**
13456      * @property disableFormats
13457      * @hide
13458      */
13459     /**
13460      * @method set
13461      * @hide
13462      */
13463
13464 });
13465 /**
13466  * Alias for {@link #applyTemplate}
13467  * Returns an HTML fragment of this template with the specified values applied.
13468  * @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'})
13469  * @return {String} The HTML fragment
13470  * @member Ext.XTemplate
13471  * @method apply
13472  */
13473 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13474
13475 /**
13476  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13477  * @param {String/HTMLElement} el A DOM element or its id
13478  * @return {Ext.Template} The created template
13479  * @static
13480  */
13481 Ext.XTemplate.from = function(el){
13482     el = Ext.getDom(el);
13483     return new Ext.XTemplate(el.value || el.innerHTML);
13484 };
13485 /**
13486  * @class Ext.util.CSS
13487  * Utility class for manipulating CSS rules
13488  * @singleton
13489  */
13490 Ext.util.CSS = function(){
13491         var rules = null;
13492         var doc = document;
13493
13494     var camelRe = /(-[a-z])/gi;
13495     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13496
13497    return {
13498    /**
13499     * Creates a stylesheet from a text blob of rules.
13500     * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13501     * @param {String} cssText The text containing the css rules
13502     * @param {String} id An id to add to the stylesheet for later removal
13503     * @return {StyleSheet}
13504     */
13505    createStyleSheet : function(cssText, id){
13506        var ss;
13507        var head = doc.getElementsByTagName("head")[0];
13508        var rules = doc.createElement("style");
13509        rules.setAttribute("type", "text/css");
13510        if(id){
13511            rules.setAttribute("id", id);
13512        }
13513        if(Ext.isIE){
13514            head.appendChild(rules);
13515            ss = rules.styleSheet;
13516            ss.cssText = cssText;
13517        }else{
13518            try{
13519                 rules.appendChild(doc.createTextNode(cssText));
13520            }catch(e){
13521                rules.cssText = cssText;
13522            }
13523            head.appendChild(rules);
13524            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13525        }
13526        this.cacheStyleSheet(ss);
13527        return ss;
13528    },
13529
13530    /**
13531     * Removes a style or link tag by id
13532     * @param {String} id The id of the tag
13533     */
13534    removeStyleSheet : function(id){
13535        var existing = doc.getElementById(id);
13536        if(existing){
13537            existing.parentNode.removeChild(existing);
13538        }
13539    },
13540
13541    /**
13542     * Dynamically swaps an existing stylesheet reference for a new one
13543     * @param {String} id The id of an existing link tag to remove
13544     * @param {String} url The href of the new stylesheet to include
13545     */
13546    swapStyleSheet : function(id, url){
13547        this.removeStyleSheet(id);
13548        var ss = doc.createElement("link");
13549        ss.setAttribute("rel", "stylesheet");
13550        ss.setAttribute("type", "text/css");
13551        ss.setAttribute("id", id);
13552        ss.setAttribute("href", url);
13553        doc.getElementsByTagName("head")[0].appendChild(ss);
13554    },
13555    
13556    /**
13557     * Refresh the rule cache if you have dynamically added stylesheets
13558     * @return {Object} An object (hash) of rules indexed by selector
13559     */
13560    refreshCache : function(){
13561        return this.getRules(true);
13562    },
13563
13564    // private
13565    cacheStyleSheet : function(ss){
13566        if(!rules){
13567            rules = {};
13568        }
13569        try{// try catch for cross domain access issue
13570            var ssRules = ss.cssRules || ss.rules;
13571            for(var j = ssRules.length-1; j >= 0; --j){
13572                rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13573            }
13574        }catch(e){}
13575    },
13576    
13577    /**
13578     * Gets all css rules for the document
13579     * @param {Boolean} refreshCache true to refresh the internal cache
13580     * @return {Object} An object (hash) of rules indexed by selector
13581     */
13582    getRules : function(refreshCache){
13583                 if(rules === null || refreshCache){
13584                         rules = {};
13585                         var ds = doc.styleSheets;
13586                         for(var i =0, len = ds.length; i < len; i++){
13587                             try{
13588                         this.cacheStyleSheet(ds[i]);
13589                     }catch(e){} 
13590                 }
13591                 }
13592                 return rules;
13593         },
13594         
13595         /**
13596     * Gets an an individual CSS rule by selector(s)
13597     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13598     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13599     * @return {CSSRule} The CSS rule or null if one is not found
13600     */
13601    getRule : function(selector, refreshCache){
13602                 var rs = this.getRules(refreshCache);
13603                 if(!Ext.isArray(selector)){
13604                     return rs[selector.toLowerCase()];
13605                 }
13606                 for(var i = 0; i < selector.length; i++){
13607                         if(rs[selector[i]]){
13608                                 return rs[selector[i].toLowerCase()];
13609                         }
13610                 }
13611                 return null;
13612         },
13613         
13614         
13615         /**
13616     * Updates a rule property
13617     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13618     * @param {String} property The css property
13619     * @param {String} value The new value for the property
13620     * @return {Boolean} true If a rule was found and updated
13621     */
13622    updateRule : function(selector, property, value){
13623                 if(!Ext.isArray(selector)){
13624                         var rule = this.getRule(selector);
13625                         if(rule){
13626                                 rule.style[property.replace(camelRe, camelFn)] = value;
13627                                 return true;
13628                         }
13629                 }else{
13630                         for(var i = 0; i < selector.length; i++){
13631                                 if(this.updateRule(selector[i], property, value)){
13632                                         return true;
13633                                 }
13634                         }
13635                 }
13636                 return false;
13637         }
13638    };   
13639 }();/**
13640  @class Ext.util.ClickRepeater
13641  @extends Ext.util.Observable
13642
13643  A wrapper class which can be applied to any element. Fires a "click" event while the
13644  mouse is pressed. The interval between firings may be specified in the config but
13645  defaults to 20 milliseconds.
13646
13647  Optionally, a CSS class may be applied to the element during the time it is pressed.
13648
13649  @cfg {Mixed} el The element to act as a button.
13650  @cfg {Number} delay The initial delay before the repeating event begins firing.
13651  Similar to an autorepeat key delay.
13652  @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13653  @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13654  @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13655            "interval" and "delay" are ignored.
13656  @cfg {Boolean} preventDefault True to prevent the default click event
13657  @cfg {Boolean} stopDefault True to stop the default click event
13658
13659  @history
13660     2007-02-02 jvs Original code contributed by Nige "Animal" White
13661     2007-02-02 jvs Renamed to ClickRepeater
13662     2007-02-03 jvs Modifications for FF Mac and Safari
13663
13664  @constructor
13665  @param {Mixed} el The element to listen on
13666  @param {Object} config
13667  */
13668 Ext.util.ClickRepeater = function(el, config)
13669 {
13670     this.el = Ext.get(el);
13671     this.el.unselectable();
13672
13673     Ext.apply(this, config);
13674
13675     this.addEvents(
13676     /**
13677      * @event mousedown
13678      * Fires when the mouse button is depressed.
13679      * @param {Ext.util.ClickRepeater} this
13680      */
13681         "mousedown",
13682     /**
13683      * @event click
13684      * Fires on a specified interval during the time the element is pressed.
13685      * @param {Ext.util.ClickRepeater} this
13686      */
13687         "click",
13688     /**
13689      * @event mouseup
13690      * Fires when the mouse key is released.
13691      * @param {Ext.util.ClickRepeater} this
13692      */
13693         "mouseup"
13694     );
13695
13696     if(!this.disabled){
13697         this.disabled = true;
13698         this.enable();
13699     }
13700
13701     // allow inline handler
13702     if(this.handler){
13703         this.on("click", this.handler,  this.scope || this);
13704     }
13705
13706     Ext.util.ClickRepeater.superclass.constructor.call(this);
13707 };
13708
13709 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13710     interval : 20,
13711     delay: 250,
13712     preventDefault : true,
13713     stopDefault : false,
13714     timer : 0,
13715
13716     /**
13717      * Enables the repeater and allows events to fire.
13718      */
13719     enable: function(){
13720         if(this.disabled){
13721             this.el.on('mousedown', this.handleMouseDown, this);
13722             if (Ext.isIE){
13723                 this.el.on('dblclick', this.handleDblClick, this);
13724             }
13725             if(this.preventDefault || this.stopDefault){
13726                 this.el.on('click', this.eventOptions, this);
13727             }
13728         }
13729         this.disabled = false;
13730     },
13731
13732     /**
13733      * Disables the repeater and stops events from firing.
13734      */
13735     disable: function(/* private */ force){
13736         if(force || !this.disabled){
13737             clearTimeout(this.timer);
13738             if(this.pressClass){
13739                 this.el.removeClass(this.pressClass);
13740             }
13741             Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13742             this.el.removeAllListeners();
13743         }
13744         this.disabled = true;
13745     },
13746
13747     /**
13748      * Convenience function for setting disabled/enabled by boolean.
13749      * @param {Boolean} disabled
13750      */
13751     setDisabled: function(disabled){
13752         this[disabled ? 'disable' : 'enable']();
13753     },
13754
13755     eventOptions: function(e){
13756         if(this.preventDefault){
13757             e.preventDefault();
13758         }
13759         if(this.stopDefault){
13760             e.stopEvent();
13761         }
13762     },
13763
13764     // private
13765     destroy : function() {
13766         this.disable(true);
13767         Ext.destroy(this.el);
13768         this.purgeListeners();
13769     },
13770
13771     handleDblClick : function(){
13772         clearTimeout(this.timer);
13773         this.el.blur();
13774
13775         this.fireEvent("mousedown", this);
13776         this.fireEvent("click", this);
13777     },
13778
13779     // private
13780     handleMouseDown : function(){
13781         clearTimeout(this.timer);
13782         this.el.blur();
13783         if(this.pressClass){
13784             this.el.addClass(this.pressClass);
13785         }
13786         this.mousedownTime = new Date();
13787
13788         Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13789         this.el.on("mouseout", this.handleMouseOut, this);
13790
13791         this.fireEvent("mousedown", this);
13792         this.fireEvent("click", this);
13793
13794         // Do not honor delay or interval if acceleration wanted.
13795         if (this.accelerate) {
13796             this.delay = 400;
13797         }
13798         this.timer = this.click.defer(this.delay || this.interval, this);
13799     },
13800
13801     // private
13802     click : function(){
13803         this.fireEvent("click", this);
13804         this.timer = this.click.defer(this.accelerate ?
13805             this.easeOutExpo(this.mousedownTime.getElapsed(),
13806                 400,
13807                 -390,
13808                 12000) :
13809             this.interval, this);
13810     },
13811
13812     easeOutExpo : function (t, b, c, d) {
13813         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13814     },
13815
13816     // private
13817     handleMouseOut : function(){
13818         clearTimeout(this.timer);
13819         if(this.pressClass){
13820             this.el.removeClass(this.pressClass);
13821         }
13822         this.el.on("mouseover", this.handleMouseReturn, this);
13823     },
13824
13825     // private
13826     handleMouseReturn : function(){
13827         this.el.un("mouseover", this.handleMouseReturn, this);
13828         if(this.pressClass){
13829             this.el.addClass(this.pressClass);
13830         }
13831         this.click();
13832     },
13833
13834     // private
13835     handleMouseUp : function(){
13836         clearTimeout(this.timer);
13837         this.el.un("mouseover", this.handleMouseReturn, this);
13838         this.el.un("mouseout", this.handleMouseOut, this);
13839         Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13840         this.el.removeClass(this.pressClass);
13841         this.fireEvent("mouseup", this);
13842     }
13843 });/**
13844  * @class Ext.KeyNav
13845  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13846  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13847  * way to implement custom navigation schemes for any UI component.</p>
13848  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13849  * pageUp, pageDown, del, home, end.  Usage:</p>
13850  <pre><code>
13851 var nav = new Ext.KeyNav("my-element", {
13852     "left" : function(e){
13853         this.moveLeft(e.ctrlKey);
13854     },
13855     "right" : function(e){
13856         this.moveRight(e.ctrlKey);
13857     },
13858     "enter" : function(e){
13859         this.save();
13860     },
13861     scope : this
13862 });
13863 </code></pre>
13864  * @constructor
13865  * @param {Mixed} el The element to bind to
13866  * @param {Object} config The config
13867  */
13868 Ext.KeyNav = function(el, config){
13869     this.el = Ext.get(el);
13870     Ext.apply(this, config);
13871     if(!this.disabled){
13872         this.disabled = true;
13873         this.enable();
13874     }
13875 };
13876
13877 Ext.KeyNav.prototype = {
13878     /**
13879      * @cfg {Boolean} disabled
13880      * True to disable this KeyNav instance (defaults to false)
13881      */
13882     disabled : false,
13883     /**
13884      * @cfg {String} defaultEventAction
13885      * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
13886      * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13887      * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13888      */
13889     defaultEventAction: "stopEvent",
13890     /**
13891      * @cfg {Boolean} forceKeyDown
13892      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13893      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13894      * handle keydown instead of keypress.
13895      */
13896     forceKeyDown : false,
13897
13898     // private
13899     relay : function(e){
13900         var k = e.getKey();
13901         var h = this.keyToHandler[k];
13902         if(h && this[h]){
13903             if(this.doRelay(e, this[h], h) !== true){
13904                 e[this.defaultEventAction]();
13905             }
13906         }
13907     },
13908
13909     // private
13910     doRelay : function(e, h, hname){
13911         return h.call(this.scope || this, e);
13912     },
13913
13914     // possible handlers
13915     enter : false,
13916     left : false,
13917     right : false,
13918     up : false,
13919     down : false,
13920     tab : false,
13921     esc : false,
13922     pageUp : false,
13923     pageDown : false,
13924     del : false,
13925     home : false,
13926     end : false,
13927
13928     // quick lookup hash
13929     keyToHandler : {
13930         37 : "left",
13931         39 : "right",
13932         38 : "up",
13933         40 : "down",
13934         33 : "pageUp",
13935         34 : "pageDown",
13936         46 : "del",
13937         36 : "home",
13938         35 : "end",
13939         13 : "enter",
13940         27 : "esc",
13941         9  : "tab"
13942     },
13943     
13944     stopKeyUp: function(e) {
13945         var k = e.getKey();
13946
13947         if (k >= 37 && k <= 40) {
13948             // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13949             // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13950             e.stopEvent();
13951         }
13952     },
13953     
13954     /**
13955      * Destroy this KeyNav (this is the same as calling disable).
13956      */
13957     destroy: function(){
13958         this.disable();    
13959     },
13960
13961         /**
13962          * Enable this KeyNav
13963          */
13964         enable: function() {
13965         if (this.disabled) {
13966             if (Ext.isSafari2) {
13967                 // call stopKeyUp() on "keyup" event
13968                 this.el.on('keyup', this.stopKeyUp, this);
13969             }
13970
13971             this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13972             this.disabled = false;
13973         }
13974     },
13975
13976         /**
13977          * Disable this KeyNav
13978          */
13979         disable: function() {
13980         if (!this.disabled) {
13981             if (Ext.isSafari2) {
13982                 // remove "keyup" event handler
13983                 this.el.un('keyup', this.stopKeyUp, this);
13984             }
13985
13986             this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13987             this.disabled = true;
13988         }
13989     },
13990     
13991     /**
13992      * Convenience function for setting disabled/enabled by boolean.
13993      * @param {Boolean} disabled
13994      */
13995     setDisabled : function(disabled){
13996         this[disabled ? "disable" : "enable"]();
13997     },
13998     
13999     // private
14000     isKeydown: function(){
14001         return this.forceKeyDown || Ext.EventManager.useKeydown;
14002     }
14003 };
14004 /**
14005  * @class Ext.KeyMap
14006  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14007  * The constructor accepts the same config object as defined by {@link #addBinding}.
14008  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14009  * combination it will call the function with this signature (if the match is a multi-key
14010  * combination the callback will still be called only once): (String key, Ext.EventObject e)
14011  * A KeyMap can also handle a string representation of keys.<br />
14012  * Usage:
14013  <pre><code>
14014 // map one key by key code
14015 var map = new Ext.KeyMap("my-element", {
14016     key: 13, // or Ext.EventObject.ENTER
14017     fn: myHandler,
14018     scope: myObject
14019 });
14020
14021 // map multiple keys to one action by string
14022 var map = new Ext.KeyMap("my-element", {
14023     key: "a\r\n\t",
14024     fn: myHandler,
14025     scope: myObject
14026 });
14027
14028 // map multiple keys to multiple actions by strings and array of codes
14029 var map = new Ext.KeyMap("my-element", [
14030     {
14031         key: [10,13],
14032         fn: function(){ alert("Return was pressed"); }
14033     }, {
14034         key: "abc",
14035         fn: function(){ alert('a, b or c was pressed'); }
14036     }, {
14037         key: "\t",
14038         ctrl:true,
14039         shift:true,
14040         fn: function(){ alert('Control + shift + tab was pressed.'); }
14041     }
14042 ]);
14043 </code></pre>
14044  * <b>Note: A KeyMap starts enabled</b>
14045  * @constructor
14046  * @param {Mixed} el The element to bind to
14047  * @param {Object} config The config (see {@link #addBinding})
14048  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14049  */
14050 Ext.KeyMap = function(el, config, eventName){
14051     this.el  = Ext.get(el);
14052     this.eventName = eventName || "keydown";
14053     this.bindings = [];
14054     if(config){
14055         this.addBinding(config);
14056     }
14057     this.enable();
14058 };
14059
14060 Ext.KeyMap.prototype = {
14061     /**
14062      * True to stop the event from bubbling and prevent the default browser action if the
14063      * key was handled by the KeyMap (defaults to false)
14064      * @type Boolean
14065      */
14066     stopEvent : false,
14067
14068     /**
14069      * Add a new binding to this KeyMap. The following config object properties are supported:
14070      * <pre>
14071 Property    Type             Description
14072 ----------  ---------------  ----------------------------------------------------------------------
14073 key         String/Array     A single keycode or an array of keycodes to handle
14074 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)
14075 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)
14076 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)
14077 handler     Function         The function to call when KeyMap finds the expected key combination
14078 fn          Function         Alias of handler (for backwards-compatibility)
14079 scope       Object           The scope of the callback function
14080 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)
14081 </pre>
14082      *
14083      * Usage:
14084      * <pre><code>
14085 // Create a KeyMap
14086 var map = new Ext.KeyMap(document, {
14087     key: Ext.EventObject.ENTER,
14088     fn: handleKey,
14089     scope: this
14090 });
14091
14092 //Add a new binding to the existing KeyMap later
14093 map.addBinding({
14094     key: 'abc',
14095     shift: true,
14096     fn: handleKey,
14097     scope: this
14098 });
14099 </code></pre>
14100      * @param {Object/Array} config A single KeyMap config or an array of configs
14101      */
14102         addBinding : function(config){
14103         if(Ext.isArray(config)){
14104             Ext.each(config, function(c){
14105                 this.addBinding(c);
14106             }, this);
14107             return;
14108         }
14109         var keyCode = config.key,
14110             fn = config.fn || config.handler,
14111             scope = config.scope;
14112
14113         if (config.stopEvent) {
14114             this.stopEvent = config.stopEvent;    
14115         }       
14116
14117         if(typeof keyCode == "string"){
14118             var ks = [];
14119             var keyString = keyCode.toUpperCase();
14120             for(var j = 0, len = keyString.length; j < len; j++){
14121                 ks.push(keyString.charCodeAt(j));
14122             }
14123             keyCode = ks;
14124         }
14125         var keyArray = Ext.isArray(keyCode);
14126         
14127         var handler = function(e){
14128             if(this.checkModifiers(config, e)){
14129                 var k = e.getKey();
14130                 if(keyArray){
14131                     for(var i = 0, len = keyCode.length; i < len; i++){
14132                         if(keyCode[i] == k){
14133                           if(this.stopEvent){
14134                               e.stopEvent();
14135                           }
14136                           fn.call(scope || window, k, e);
14137                           return;
14138                         }
14139                     }
14140                 }else{
14141                     if(k == keyCode){
14142                         if(this.stopEvent){
14143                            e.stopEvent();
14144                         }
14145                         fn.call(scope || window, k, e);
14146                     }
14147                 }
14148             }
14149         };
14150         this.bindings.push(handler);
14151         },
14152     
14153     // private
14154     checkModifiers: function(config, e){
14155         var val, key, keys = ['shift', 'ctrl', 'alt'];
14156         for (var i = 0, len = keys.length; i < len; ++i){
14157             key = keys[i];
14158             val = config[key];
14159             if(!(val === undefined || (val === e[key + 'Key']))){
14160                 return false;
14161             }
14162         }
14163         return true;
14164     },
14165
14166     /**
14167      * Shorthand for adding a single key listener
14168      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14169      * following options:
14170      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14171      * @param {Function} fn The function to call
14172      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14173      */
14174     on : function(key, fn, scope){
14175         var keyCode, shift, ctrl, alt;
14176         if(typeof key == "object" && !Ext.isArray(key)){
14177             keyCode = key.key;
14178             shift = key.shift;
14179             ctrl = key.ctrl;
14180             alt = key.alt;
14181         }else{
14182             keyCode = key;
14183         }
14184         this.addBinding({
14185             key: keyCode,
14186             shift: shift,
14187             ctrl: ctrl,
14188             alt: alt,
14189             fn: fn,
14190             scope: scope
14191         });
14192     },
14193
14194     // private
14195     handleKeyDown : function(e){
14196             if(this.enabled){ //just in case
14197             var b = this.bindings;
14198             for(var i = 0, len = b.length; i < len; i++){
14199                 b[i].call(this, e);
14200             }
14201             }
14202         },
14203
14204         /**
14205          * Returns true if this KeyMap is enabled
14206          * @return {Boolean}
14207          */
14208         isEnabled : function(){
14209             return this.enabled;
14210         },
14211
14212         /**
14213          * Enables this KeyMap
14214          */
14215         enable: function(){
14216                 if(!this.enabled){
14217                     this.el.on(this.eventName, this.handleKeyDown, this);
14218                     this.enabled = true;
14219                 }
14220         },
14221
14222         /**
14223          * Disable this KeyMap
14224          */
14225         disable: function(){
14226                 if(this.enabled){
14227                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14228                     this.enabled = false;
14229                 }
14230         },
14231     
14232     /**
14233      * Convenience function for setting disabled/enabled by boolean.
14234      * @param {Boolean} disabled
14235      */
14236     setDisabled : function(disabled){
14237         this[disabled ? "disable" : "enable"]();
14238     }
14239 };/**
14240  * @class Ext.util.TextMetrics
14241  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14242  * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14243  * should not contain any HTML, otherwise it may not be measured correctly.
14244  * @singleton
14245  */
14246 Ext.util.TextMetrics = function(){
14247     var shared;
14248     return {
14249         /**
14250          * Measures the size of the specified text
14251          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14252          * that can affect the size of the rendered text
14253          * @param {String} text The text to measure
14254          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14255          * in order to accurately measure the text height
14256          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14257          */
14258         measure : function(el, text, fixedWidth){
14259             if(!shared){
14260                 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14261             }
14262             shared.bind(el);
14263             shared.setFixedWidth(fixedWidth || 'auto');
14264             return shared.getSize(text);
14265         },
14266
14267         /**
14268          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14269          * the overhead of multiple calls to initialize the style properties on each measurement.
14270          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14271          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14272          * in order to accurately measure the text height
14273          * @return {Ext.util.TextMetrics.Instance} instance The new instance
14274          */
14275         createInstance : function(el, fixedWidth){
14276             return Ext.util.TextMetrics.Instance(el, fixedWidth);
14277         }
14278     };
14279 }();
14280
14281 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14282     var ml = new Ext.Element(document.createElement('div'));
14283     document.body.appendChild(ml.dom);
14284     ml.position('absolute');
14285     ml.setLeftTop(-1000, -1000);
14286     ml.hide();
14287
14288     if(fixedWidth){
14289         ml.setWidth(fixedWidth);
14290     }
14291
14292     var instance = {
14293         /**
14294          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14295          * Returns the size of the specified text based on the internal element's style and width properties
14296          * @param {String} text The text to measure
14297          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14298          */
14299         getSize : function(text){
14300             ml.update(text);
14301             var s = ml.getSize();
14302             ml.update('');
14303             return s;
14304         },
14305
14306         /**
14307          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14308          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14309          * that can affect the size of the rendered text
14310          * @param {String/HTMLElement} el The element, dom node or id
14311          */
14312         bind : function(el){
14313             ml.setStyle(
14314                 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14315             );
14316         },
14317
14318         /**
14319          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14320          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14321          * to set a fixed width in order to accurately measure the text height.
14322          * @param {Number} width The width to set on the element
14323          */
14324         setFixedWidth : function(width){
14325             ml.setWidth(width);
14326         },
14327
14328         /**
14329          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14330          * Returns the measured width of the specified text
14331          * @param {String} text The text to measure
14332          * @return {Number} width The width in pixels
14333          */
14334         getWidth : function(text){
14335             ml.dom.style.width = 'auto';
14336             return this.getSize(text).width;
14337         },
14338
14339         /**
14340          * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14341          * Returns the measured height of the specified text.  For multiline text, be sure to call
14342          * {@link #setFixedWidth} if necessary.
14343          * @param {String} text The text to measure
14344          * @return {Number} height The height in pixels
14345          */
14346         getHeight : function(text){
14347             return this.getSize(text).height;
14348         }
14349     };
14350
14351     instance.bind(bindTo);
14352
14353     return instance;
14354 };
14355
14356 Ext.Element.addMethods({
14357     /**
14358      * Returns the width in pixels of the passed text, or the width of the text in this Element.
14359      * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14360      * @param {Number} min (Optional) The minumum value to return.
14361      * @param {Number} max (Optional) The maximum value to return.
14362      * @return {Number} The text width in pixels.
14363      * @member Ext.Element getTextWidth
14364      */
14365     getTextWidth : function(text, min, max){
14366         return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14367     }
14368 });
14369 /**
14370  * @class Ext.util.Cookies
14371  * Utility class for managing and interacting with cookies.
14372  * @singleton
14373  */
14374 Ext.util.Cookies = {
14375     /**
14376      * Create a cookie with the specified name and value. Additional settings
14377      * for the cookie may be optionally specified (for example: expiration,
14378      * access restriction, SSL).
14379      * @param {String} name The name of the cookie to set. 
14380      * @param {Mixed} value The value to set for the cookie.
14381      * @param {Object} expires (Optional) Specify an expiration date the
14382      * cookie is to persist until.  Note that the specified Date object will
14383      * be converted to Greenwich Mean Time (GMT). 
14384      * @param {String} path (Optional) Setting a path on the cookie restricts
14385      * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
14386      * @param {String} domain (Optional) Setting a domain restricts access to
14387      * pages on a given domain (typically used to allow cookie access across
14388      * subdomains). For example, "extjs.com" will create a cookie that can be
14389      * accessed from any subdomain of extjs.com, including www.extjs.com,
14390      * support.extjs.com, etc.
14391      * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14392      * should only be accessible via SSL on a page using the HTTPS protocol.
14393      * Defaults to <tt>false</tt>. Note that this will only work if the page
14394      * calling this code uses the HTTPS protocol, otherwise the cookie will be
14395      * created with default options.
14396      */
14397     set : function(name, value){
14398         var argv = arguments;
14399         var argc = arguments.length;
14400         var expires = (argc > 2) ? argv[2] : null;
14401         var path = (argc > 3) ? argv[3] : '/';
14402         var domain = (argc > 4) ? argv[4] : null;
14403         var secure = (argc > 5) ? argv[5] : false;
14404         document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14405     },
14406
14407     /**
14408      * Retrieves cookies that are accessible by the current page. If a cookie
14409      * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
14410      * example retrieves the cookie called "valid" and stores the String value
14411      * in the variable <tt>validStatus</tt>.
14412      * <pre><code>
14413      * var validStatus = Ext.util.Cookies.get("valid");
14414      * </code></pre>
14415      * @param {String} name The name of the cookie to get
14416      * @return {Mixed} Returns the cookie value for the specified name;
14417      * null if the cookie name does not exist.
14418      */
14419     get : function(name){
14420         var arg = name + "=";
14421         var alen = arg.length;
14422         var clen = document.cookie.length;
14423         var i = 0;
14424         var j = 0;
14425         while(i < clen){
14426             j = i + alen;
14427             if(document.cookie.substring(i, j) == arg){
14428                 return Ext.util.Cookies.getCookieVal(j);
14429             }
14430             i = document.cookie.indexOf(" ", i) + 1;
14431             if(i === 0){
14432                 break;
14433             }
14434         }
14435         return null;
14436     },
14437
14438     /**
14439      * Removes a cookie with the provided name from the browser
14440      * if found by setting its expiration date to sometime in the past. 
14441      * @param {String} name The name of the cookie to remove
14442      */
14443     clear : function(name){
14444         if(Ext.util.Cookies.get(name)){
14445             document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14446         }
14447     },
14448     /**
14449      * @private
14450      */
14451     getCookieVal : function(offset){
14452         var endstr = document.cookie.indexOf(";", offset);
14453         if(endstr == -1){
14454             endstr = document.cookie.length;
14455         }
14456         return unescape(document.cookie.substring(offset, endstr));
14457     }
14458 };/**
14459  * Framework-wide error-handler.  Developers can override this method to provide
14460  * custom exception-handling.  Framework errors will often extend from the base
14461  * Ext.Error class.
14462  * @param {Object/Error} e The thrown exception object.
14463  */
14464 Ext.handleError = function(e) {
14465     throw e;
14466 };
14467
14468 /**
14469  * @class Ext.Error
14470  * @extends Error
14471  * <p>A base error class. Future implementations are intended to provide more
14472  * robust error handling throughout the framework (<b>in the debug build only</b>)
14473  * to check for common errors and problems. The messages issued by this class
14474  * will aid error checking. Error checks will be automatically removed in the
14475  * production build so that performance is not negatively impacted.</p>
14476  * <p>Some sample messages currently implemented:</p><pre>
14477 "DataProxy attempted to execute an API-action but found an undefined
14478 url / function. Please review your Proxy url/api-configuration."
14479  * </pre><pre>
14480 "Could not locate your "root" property in your server response.
14481 Please review your JsonReader config to ensure the config-property
14482 "root" matches the property your server-response.  See the JsonReader
14483 docs for additional assistance."
14484  * </pre>
14485  * <p>An example of the code used for generating error messages:</p><pre><code>
14486 try {
14487     generateError({
14488         foo: 'bar'
14489     });
14490 }
14491 catch (e) {
14492     console.error(e);
14493 }
14494 function generateError(data) {
14495     throw new Ext.Error('foo-error', data);
14496 }
14497  * </code></pre>
14498  * @param {String} message
14499  */
14500 Ext.Error = function(message) {
14501     // Try to read the message from Ext.Error.lang
14502     this.message = (this.lang[message]) ? this.lang[message] : message;
14503 };
14504
14505 Ext.Error.prototype = new Error();
14506 Ext.apply(Ext.Error.prototype, {
14507     // protected.  Extensions place their error-strings here.
14508     lang: {},
14509
14510     name: 'Ext.Error',
14511     /**
14512      * getName
14513      * @return {String}
14514      */
14515     getName : function() {
14516         return this.name;
14517     },
14518     /**
14519      * getMessage
14520      * @return {String}
14521      */
14522     getMessage : function() {
14523         return this.message;
14524     },
14525     /**
14526      * toJson
14527      * @return {String}
14528      */
14529     toJson : function() {
14530         return Ext.encode(this);
14531     }
14532 });
14533 /**
14534  * @class Ext.ComponentMgr
14535  * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
14536  * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
14537  * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
14538  * <p>This object also provides a registry of available Component <i>classes</i>
14539  * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
14540  * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
14541  * when creating a full, nested config object for a complete Ext page.</p>
14542  * <p>A child Component may be specified simply as a <i>config object</i>
14543  * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
14544  * needs rendering, the correct type can be looked up for lazy instantiation.</p>
14545  * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
14546  * @singleton
14547  */
14548 Ext.ComponentMgr = function(){
14549     var all = new Ext.util.MixedCollection();
14550     var types = {};
14551     var ptypes = {};
14552
14553     return {
14554         /**
14555          * Registers a component.
14556          * @param {Ext.Component} c The component
14557          */
14558         register : function(c){
14559             all.add(c);
14560         },
14561
14562         /**
14563          * Unregisters a component.
14564          * @param {Ext.Component} c The component
14565          */
14566         unregister : function(c){
14567             all.remove(c);
14568         },
14569
14570         /**
14571          * Returns a component by {@link Ext.Component#id id}.
14572          * For additional details see {@link Ext.util.MixedCollection#get}.
14573          * @param {String} id The component {@link Ext.Component#id id}
14574          * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
14575          * Class was found.
14576          */
14577         get : function(id){
14578             return all.get(id);
14579         },
14580
14581         /**
14582          * Registers a function that will be called when a Component with the specified id is added to ComponentMgr. This will happen on instantiation.
14583          * @param {String} id The component {@link Ext.Component#id id}
14584          * @param {Function} fn The callback function
14585          * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
14586          */
14587         onAvailable : function(id, fn, scope){
14588             all.on("add", function(index, o){
14589                 if(o.id == id){
14590                     fn.call(scope || o, o);
14591                     all.un("add", fn, scope);
14592                 }
14593             });
14594         },
14595
14596         /**
14597          * The MixedCollection used internally for the component cache. An example usage may be subscribing to
14598          * events on the MixedCollection to monitor addition or removal.  Read-only.
14599          * @type {MixedCollection}
14600          */
14601         all : all,
14602         
14603         /**
14604          * The xtypes that have been registered with the component manager.
14605          * @type {Object}
14606          */
14607         types : types,
14608         
14609         /**
14610          * The ptypes that have been registered with the component manager.
14611          * @type {Object}
14612          */
14613         ptypes: ptypes,
14614         
14615         /**
14616          * Checks if a Component type is registered.
14617          * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
14618          * @return {Boolean} Whether the type is registered.
14619          */
14620         isRegistered : function(xtype){
14621             return types[xtype] !== undefined;    
14622         },
14623         
14624         /**
14625          * Checks if a Plugin type is registered.
14626          * @param {Ext.Component} ptype The mnemonic string by which the Plugin class may be looked up
14627          * @return {Boolean} Whether the type is registered.
14628          */
14629         isPluginRegistered : function(ptype){
14630             return ptypes[ptype] !== undefined;    
14631         },        
14632
14633         /**
14634          * <p>Registers a new Component constructor, keyed by a new
14635          * {@link Ext.Component#xtype}.</p>
14636          * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
14637          * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
14638          * child Components.
14639          * see {@link Ext.Container#items}</p>
14640          * @param {String} xtype The mnemonic string by which the Component class may be looked up.
14641          * @param {Constructor} cls The new Component class.
14642          */
14643         registerType : function(xtype, cls){
14644             types[xtype] = cls;
14645             cls.xtype = xtype;
14646         },
14647
14648         /**
14649          * Creates a new Component from the specified config object using the
14650          * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
14651          * @param {Object} config A configuration object for the Component you wish to create.
14652          * @param {Constructor} defaultType The constructor to provide the default Component type if
14653          * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
14654          * @return {Ext.Component} The newly instantiated Component.
14655          */
14656         create : function(config, defaultType){
14657             return config.render ? config : new types[config.xtype || defaultType](config);
14658         },
14659
14660         /**
14661          * <p>Registers a new Plugin constructor, keyed by a new
14662          * {@link Ext.Component#ptype}.</p>
14663          * <p>Use this method (or its alias {@link Ext#preg Ext.preg}) to register new
14664          * plugins for {@link Ext.Component}s so that lazy instantiation may be used when specifying
14665          * Plugins.</p>
14666          * @param {String} ptype The mnemonic string by which the Plugin class may be looked up.
14667          * @param {Constructor} cls The new Plugin class.
14668          */
14669         registerPlugin : function(ptype, cls){
14670             ptypes[ptype] = cls;
14671             cls.ptype = ptype;
14672         },
14673
14674         /**
14675          * Creates a new Plugin from the specified config object using the
14676          * config object's {@link Ext.component#ptype ptype} to determine the class to instantiate.
14677          * @param {Object} config A configuration object for the Plugin you wish to create.
14678          * @param {Constructor} defaultType The constructor to provide the default Plugin type if
14679          * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
14680          * @return {Ext.Component} The newly instantiated Plugin.
14681          */
14682         createPlugin : function(config, defaultType){
14683             var PluginCls = ptypes[config.ptype || defaultType];
14684             if (PluginCls.init) {
14685                 return PluginCls;                
14686             } else {
14687                 return new PluginCls(config);
14688             }            
14689         }
14690     };
14691 }();
14692
14693 /**
14694  * Shorthand for {@link Ext.ComponentMgr#registerType}
14695  * @param {String} xtype The {@link Ext.component#xtype mnemonic string} by which the Component class
14696  * may be looked up.
14697  * @param {Constructor} cls The new Component class.
14698  * @member Ext
14699  * @method reg
14700  */
14701 Ext.reg = Ext.ComponentMgr.registerType; // this will be called a lot internally, shorthand to keep the bytes down
14702 /**
14703  * Shorthand for {@link Ext.ComponentMgr#registerPlugin}
14704  * @param {String} ptype The {@link Ext.component#ptype mnemonic string} by which the Plugin class
14705  * may be looked up.
14706  * @param {Constructor} cls The new Plugin class.
14707  * @member Ext
14708  * @method preg
14709  */
14710 Ext.preg = Ext.ComponentMgr.registerPlugin;
14711 /**
14712  * Shorthand for {@link Ext.ComponentMgr#create}
14713  * Creates a new Component from the specified config object using the
14714  * config object's {@link Ext.component#xtype xtype} to determine the class to instantiate.
14715  * @param {Object} config A configuration object for the Component you wish to create.
14716  * @param {Constructor} defaultType The constructor to provide the default Component type if
14717  * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
14718  * @return {Ext.Component} The newly instantiated Component.
14719  * @member Ext
14720  * @method create
14721  */
14722 Ext.create = Ext.ComponentMgr.create;/**
14723  * @class Ext.Component
14724  * @extends Ext.util.Observable
14725  * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
14726  * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.Container Container} class.
14727  * Components may be added to a Container through the {@link Ext.Container#items items} config option at the time the Container is created,
14728  * or they may be added dynamically via the {@link Ext.Container#add add} method.</p>
14729  * <p>The Component base class has built-in support for basic hide/show and enable/disable behavior.</p>
14730  * <p>All Components are registered with the {@link Ext.ComponentMgr} on construction so that they can be referenced at any time via
14731  * {@link Ext#getCmp}, passing the {@link #id}.</p>
14732  * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component (or
14733  * {@link Ext.BoxComponent} if managed box model handling is required, ie height and width management).</p>
14734  * <p>See the <a href="http://extjs.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
14735  * and to either extend or augment ExtJs base classes to create custom Components.</p>
14736  * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
14737  * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
14738  * <pre>
14739 xtype            Class
14740 -------------    ------------------
14741 box              {@link Ext.BoxComponent}
14742 button           {@link Ext.Button}
14743 buttongroup      {@link Ext.ButtonGroup}
14744 colorpalette     {@link Ext.ColorPalette}
14745 component        {@link Ext.Component}
14746 container        {@link Ext.Container}
14747 cycle            {@link Ext.CycleButton}
14748 dataview         {@link Ext.DataView}
14749 datepicker       {@link Ext.DatePicker}
14750 editor           {@link Ext.Editor}
14751 editorgrid       {@link Ext.grid.EditorGridPanel}
14752 flash            {@link Ext.FlashComponent}
14753 grid             {@link Ext.grid.GridPanel}
14754 listview         {@link Ext.ListView}
14755 panel            {@link Ext.Panel}
14756 progress         {@link Ext.ProgressBar}
14757 propertygrid     {@link Ext.grid.PropertyGrid}
14758 slider           {@link Ext.Slider}
14759 spacer           {@link Ext.Spacer}
14760 splitbutton      {@link Ext.SplitButton}
14761 tabpanel         {@link Ext.TabPanel}
14762 treepanel        {@link Ext.tree.TreePanel}
14763 viewport         {@link Ext.ViewPort}
14764 window           {@link Ext.Window}
14765
14766 Toolbar components
14767 ---------------------------------------
14768 paging           {@link Ext.PagingToolbar}
14769 toolbar          {@link Ext.Toolbar}
14770 tbbutton         {@link Ext.Toolbar.Button}        (deprecated; use button)
14771 tbfill           {@link Ext.Toolbar.Fill}
14772 tbitem           {@link Ext.Toolbar.Item}
14773 tbseparator      {@link Ext.Toolbar.Separator}
14774 tbspacer         {@link Ext.Toolbar.Spacer}
14775 tbsplit          {@link Ext.Toolbar.SplitButton}   (deprecated; use splitbutton)
14776 tbtext           {@link Ext.Toolbar.TextItem}
14777
14778 Menu components
14779 ---------------------------------------
14780 menu             {@link Ext.menu.Menu}
14781 colormenu        {@link Ext.menu.ColorMenu}
14782 datemenu         {@link Ext.menu.DateMenu}
14783 menubaseitem     {@link Ext.menu.BaseItem}
14784 menucheckitem    {@link Ext.menu.CheckItem}
14785 menuitem         {@link Ext.menu.Item}
14786 menuseparator    {@link Ext.menu.Separator}
14787 menutextitem     {@link Ext.menu.TextItem}
14788
14789 Form components
14790 ---------------------------------------
14791 form             {@link Ext.form.FormPanel}
14792 checkbox         {@link Ext.form.Checkbox}
14793 checkboxgroup    {@link Ext.form.CheckboxGroup}
14794 combo            {@link Ext.form.ComboBox}
14795 datefield        {@link Ext.form.DateField}
14796 displayfield     {@link Ext.form.DisplayField}
14797 field            {@link Ext.form.Field}
14798 fieldset         {@link Ext.form.FieldSet}
14799 hidden           {@link Ext.form.Hidden}
14800 htmleditor       {@link Ext.form.HtmlEditor}
14801 label            {@link Ext.form.Label}
14802 numberfield      {@link Ext.form.NumberField}
14803 radio            {@link Ext.form.Radio}
14804 radiogroup       {@link Ext.form.RadioGroup}
14805 textarea         {@link Ext.form.TextArea}
14806 textfield        {@link Ext.form.TextField}
14807 timefield        {@link Ext.form.TimeField}
14808 trigger          {@link Ext.form.TriggerField}
14809
14810 Chart components
14811 ---------------------------------------
14812 chart            {@link Ext.chart.Chart}
14813 barchart         {@link Ext.chart.BarChart}
14814 cartesianchart   {@link Ext.chart.CartesianChart}
14815 columnchart      {@link Ext.chart.ColumnChart}
14816 linechart        {@link Ext.chart.LineChart}
14817 piechart         {@link Ext.chart.PieChart}
14818
14819 Store xtypes
14820 ---------------------------------------
14821 arraystore       {@link Ext.data.ArrayStore}
14822 directstore      {@link Ext.data.DirectStore}
14823 groupingstore    {@link Ext.data.GroupingStore}
14824 jsonstore        {@link Ext.data.JsonStore}
14825 simplestore      {@link Ext.data.SimpleStore}      (deprecated; use arraystore)
14826 store            {@link Ext.data.Store}
14827 xmlstore         {@link Ext.data.XmlStore}
14828 </pre>
14829  * @constructor
14830  * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
14831  * <div class="mdetail-params"><ul>
14832  * <li><b>an element</b> :
14833  * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
14834  * <li><b>a string</b> :
14835  * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
14836  * <li><b>anything else</b> :
14837  * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
14838  * </ul></div>
14839  */
14840 Ext.Component = function(config){
14841     config = config || {};
14842     if(config.initialConfig){
14843         if(config.isAction){           // actions
14844             this.baseAction = config;
14845         }
14846         config = config.initialConfig; // component cloning / action set up
14847     }else if(config.tagName || config.dom || Ext.isString(config)){ // element object
14848         config = {applyTo: config, id: config.id || config};
14849     }
14850
14851     /**
14852      * This Component's initial configuration specification. Read-only.
14853      * @type Object
14854      * @property initialConfig
14855      */
14856     this.initialConfig = config;
14857
14858     Ext.apply(this, config);
14859     this.addEvents(
14860         /**
14861          * @event added
14862          * Fires when a component is added to an Ext.Container
14863          * @param {Ext.Component} this
14864          * @param {Ext.Container} ownerCt Container which holds the component
14865          * @param {number} index Position at which the component was added
14866          */
14867         'added',
14868         /**
14869          * @event disable
14870          * Fires after the component is disabled.
14871          * @param {Ext.Component} this
14872          */
14873         'disable',
14874         /**
14875          * @event enable
14876          * Fires after the component is enabled.
14877          * @param {Ext.Component} this
14878          */
14879         'enable',
14880         /**
14881          * @event beforeshow
14882          * Fires before the component is shown by calling the {@link #show} method.
14883          * Return false from an event handler to stop the show.
14884          * @param {Ext.Component} this
14885          */
14886         'beforeshow',
14887         /**
14888          * @event show
14889          * Fires after the component is shown when calling the {@link #show} method.
14890          * @param {Ext.Component} this
14891          */
14892         'show',
14893         /**
14894          * @event beforehide
14895          * Fires before the component is hidden by calling the {@link #hide} method.
14896          * Return false from an event handler to stop the hide.
14897          * @param {Ext.Component} this
14898          */
14899         'beforehide',
14900         /**
14901          * @event hide
14902          * Fires after the component is hidden.
14903          * Fires after the component is hidden when calling the {@link #hide} method.
14904          * @param {Ext.Component} this
14905          */
14906         'hide',
14907         /**
14908          * @event removed
14909          * Fires when a component is removed from an Ext.Container
14910          * @param {Ext.Component} this
14911          * @param {Ext.Container} ownerCt Container which holds the component
14912          */
14913         'removed',
14914         /**
14915          * @event beforerender
14916          * Fires before the component is {@link #rendered}. Return false from an
14917          * event handler to stop the {@link #render}.
14918          * @param {Ext.Component} this
14919          */
14920         'beforerender',
14921         /**
14922          * @event render
14923          * Fires after the component markup is {@link #rendered}.
14924          * @param {Ext.Component} this
14925          */
14926         'render',
14927         /**
14928          * @event afterrender
14929          * <p>Fires after the component rendering is finished.</p>
14930          * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
14931          * by any afterRender method defined for the Component, and, if {@link #stateful}, after state
14932          * has been restored.</p>
14933          * @param {Ext.Component} this
14934          */
14935         'afterrender',
14936         /**
14937          * @event beforedestroy
14938          * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
14939          * @param {Ext.Component} this
14940          */
14941         'beforedestroy',
14942         /**
14943          * @event destroy
14944          * Fires after the component is {@link #destroy}ed.
14945          * @param {Ext.Component} this
14946          */
14947         'destroy',
14948         /**
14949          * @event beforestaterestore
14950          * Fires before the state of the component is restored. Return false from an event handler to stop the restore.
14951          * @param {Ext.Component} this
14952          * @param {Object} state The hash of state values returned from the StateProvider. If this
14953          * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
14954          * that simply copies property values into this Component. The method maybe overriden to
14955          * provide custom state restoration.
14956          */
14957         'beforestaterestore',
14958         /**
14959          * @event staterestore
14960          * Fires after the state of the component is restored.
14961          * @param {Ext.Component} this
14962          * @param {Object} state The hash of state values returned from the StateProvider. This is passed
14963          * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
14964          * Component. The method maybe overriden to provide custom state restoration.
14965          */
14966         'staterestore',
14967         /**
14968          * @event beforestatesave
14969          * Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
14970          * @param {Ext.Component} this
14971          * @param {Object} state The hash of state values. This is determined by calling
14972          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14973          * developer to return whetever representation of state is required, by default, Ext.Component
14974          * has a null implementation.
14975          */
14976         'beforestatesave',
14977         /**
14978          * @event statesave
14979          * Fires after the state of the component is saved to the configured state provider.
14980          * @param {Ext.Component} this
14981          * @param {Object} state The hash of state values. This is determined by calling
14982          * <b><tt>getState()</tt></b> on the Component. This method must be provided by the
14983          * developer to return whetever representation of state is required, by default, Ext.Component
14984          * has a null implementation.
14985          */
14986         'statesave'
14987     );
14988     this.getId();
14989     Ext.ComponentMgr.register(this);
14990     Ext.Component.superclass.constructor.call(this);
14991
14992     if(this.baseAction){
14993         this.baseAction.addComponent(this);
14994     }
14995
14996     this.initComponent();
14997
14998     if(this.plugins){
14999         if(Ext.isArray(this.plugins)){
15000             for(var i = 0, len = this.plugins.length; i < len; i++){
15001                 this.plugins[i] = this.initPlugin(this.plugins[i]);
15002             }
15003         }else{
15004             this.plugins = this.initPlugin(this.plugins);
15005         }
15006     }
15007
15008     if(this.stateful !== false){
15009         this.initState();
15010     }
15011
15012     if(this.applyTo){
15013         this.applyToMarkup(this.applyTo);
15014         delete this.applyTo;
15015     }else if(this.renderTo){
15016         this.render(this.renderTo);
15017         delete this.renderTo;
15018     }
15019 };
15020
15021 // private
15022 Ext.Component.AUTO_ID = 1000;
15023
15024 Ext.extend(Ext.Component, Ext.util.Observable, {
15025     // Configs below are used for all Components when rendered by FormLayout.
15026     /**
15027      * @cfg {String} fieldLabel <p>The label text to display next to this Component (defaults to '').</p>
15028      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container which
15029      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
15030      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
15031      * <p>Also see <tt>{@link #hideLabel}</tt> and
15032      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
15033      * Example use:<pre><code>
15034 new Ext.FormPanel({
15035     height: 100,
15036     renderTo: Ext.getBody(),
15037     items: [{
15038         xtype: 'textfield',
15039         fieldLabel: 'Name'
15040     }]
15041 });
15042 </code></pre>
15043      */
15044     /**
15045      * @cfg {String} labelStyle <p>A CSS style specification string to apply directly to this field's
15046      * label.  Defaults to the container's labelStyle value if set (e.g.,
15047      * <tt>{@link Ext.layout.FormLayout#labelStyle}</tt> , or '').</p>
15048      * <br><p><b>Note</b>: see the note for <code>{@link #clearCls}</code>.</p><br>
15049      * <p>Also see <code>{@link #hideLabel}</code> and
15050      * <code>{@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</code></p>
15051      * Example use:<pre><code>
15052 new Ext.FormPanel({
15053     height: 100,
15054     renderTo: Ext.getBody(),
15055     items: [{
15056         xtype: 'textfield',
15057         fieldLabel: 'Name',
15058         labelStyle: 'font-weight:bold;'
15059     }]
15060 });
15061 </code></pre>
15062      */
15063     /**
15064      * @cfg {String} labelSeparator <p>The separator to display after the text of each
15065      * <tt>{@link #fieldLabel}</tt>.  This property may be configured at various levels.
15066      * The order of precedence is:
15067      * <div class="mdetail-params"><ul>
15068      * <li>field / component level</li>
15069      * <li>container level</li>
15070      * <li>{@link Ext.layout.FormLayout#labelSeparator layout level} (defaults to colon <tt>':'</tt>)</li>
15071      * </ul></div>
15072      * To display no separator for this field's label specify empty string ''.</p>
15073      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
15074      * <p>Also see <tt>{@link #hideLabel}</tt> and
15075      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}.</p>
15076      * Example use:<pre><code>
15077 new Ext.FormPanel({
15078     height: 100,
15079     renderTo: Ext.getBody(),
15080     layoutConfig: {
15081         labelSeparator: '~'   // layout config has lowest priority (defaults to ':')
15082     },
15083     {@link Ext.layout.FormLayout#labelSeparator labelSeparator}: '>>',     // config at container level
15084     items: [{
15085         xtype: 'textfield',
15086         fieldLabel: 'Field 1',
15087         labelSeparator: '...' // field/component level config supersedes others
15088     },{
15089         xtype: 'textfield',
15090         fieldLabel: 'Field 2' // labelSeparator will be '='
15091     }]
15092 });
15093 </code></pre>
15094      */
15095     /**
15096      * @cfg {Boolean} hideLabel <p><tt>true</tt> to completely hide the label element
15097      * ({@link #fieldLabel label} and {@link #labelSeparator separator}). Defaults to <tt>false</tt>.
15098      * By default, even if you do not specify a <tt>{@link #fieldLabel}</tt> the space will still be
15099      * reserved so that the field will line up with other fields that do have labels.
15100      * Setting this to <tt>true</tt> will cause the field to not reserve that space.</p>
15101      * <br><p><b>Note</b>: see the note for <tt>{@link #clearCls}</tt>.</p><br>
15102      * Example use:<pre><code>
15103 new Ext.FormPanel({
15104     height: 100,
15105     renderTo: Ext.getBody(),
15106     items: [{
15107         xtype: 'textfield'
15108         hideLabel: true
15109     }]
15110 });
15111 </code></pre>
15112      */
15113     /**
15114      * @cfg {String} clearCls <p>The CSS class used to to apply to the special clearing div rendered
15115      * directly after each form field wrapper to provide field clearing (defaults to
15116      * <tt>'x-form-clear-left'</tt>).</p>
15117      * <br><p><b>Note</b>: this config is only used when this Component is rendered by a Container
15118      * which has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout
15119      * manager (e.g. {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) and either a
15120      * <tt>{@link #fieldLabel}</tt> is specified or <tt>isFormField=true</tt> is specified.</p><br>
15121      * <p>See {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} also.</p>
15122      */
15123     /**
15124      * @cfg {String} itemCls
15125      * <p><b>Note</b>: this config is only used when this Component is rendered by a Container which
15126      * has been configured to use the <b>{@link Ext.layout.FormLayout FormLayout}</b> layout manager (e.g.
15127      * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>).</p><br>
15128      * <p>An additional CSS class to apply to the div wrapping the form item
15129      * element of this field.  If supplied, <tt>itemCls</tt> at the <b>field</b> level will override
15130      * the default <tt>itemCls</tt> supplied at the <b>container</b> level. The value specified for
15131      * <tt>itemCls</tt> will be added to the default class (<tt>'x-form-item'</tt>).</p>
15132      * <p>Since it is applied to the item wrapper (see
15133      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl}), it allows
15134      * you to write standard CSS rules that can apply to the field, the label (if specified), or
15135      * any other element within the markup for the field.</p>
15136      * <br><p><b>Note</b>: see the note for <tt>{@link #fieldLabel}</tt>.</p><br>
15137      * Example use:<pre><code>
15138 // Apply a style to the field&#39;s label:
15139 &lt;style>
15140     .required .x-form-item-label {font-weight:bold;color:red;}
15141 &lt;/style>
15142
15143 new Ext.FormPanel({
15144     height: 100,
15145     renderTo: Ext.getBody(),
15146     items: [{
15147         xtype: 'textfield',
15148         fieldLabel: 'Name',
15149         itemCls: 'required' //this label will be styled
15150     },{
15151         xtype: 'textfield',
15152         fieldLabel: 'Favorite Color'
15153     }]
15154 });
15155 </code></pre>
15156      */
15157
15158     /**
15159      * @cfg {String} id
15160      * <p>The <b>unique</b> id of this component (defaults to an {@link #getId auto-assigned id}).
15161      * You should assign an id if you need to be able to access the component later and you do
15162      * not have an object reference available (e.g., using {@link Ext}.{@link Ext#getCmp getCmp}).</p>
15163      * <p>Note that this id will also be used as the element id for the containing HTML element
15164      * that is rendered to the page for this component. This allows you to write id-based CSS
15165      * rules to style the specific instance of this component uniquely, and also to select
15166      * sub-elements using this component's id as the parent.</p>
15167      * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see
15168      * <code>{@link #itemId}</code> and <code>{@link #ref}</code>.</p>
15169      * <p><b>Note</b>: to access the container of an item see <code>{@link #ownerCt}</code>.</p>
15170      */
15171     /**
15172      * @cfg {String} itemId
15173      * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
15174      * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
15175      * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
15176      * {@link Ext.Container}.{@link Ext.Container#getComponent getComponent} which will retrieve
15177      * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
15178      * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
15179      * avoiding potential conflicts with {@link Ext.ComponentMgr} which requires a <b>unique</b>
15180      * <code>{@link #id}</code>.</p>
15181      * <pre><code>
15182 var c = new Ext.Panel({ //
15183     {@link Ext.BoxComponent#height height}: 300,
15184     {@link #renderTo}: document.body,
15185     {@link Ext.Container#layout layout}: 'auto',
15186     {@link Ext.Container#items items}: [
15187         {
15188             itemId: 'p1',
15189             {@link Ext.Panel#title title}: 'Panel 1',
15190             {@link Ext.BoxComponent#height height}: 150
15191         },
15192         {
15193             itemId: 'p2',
15194             {@link Ext.Panel#title title}: 'Panel 2',
15195             {@link Ext.BoxComponent#height height}: 150
15196         }
15197     ]
15198 })
15199 p1 = c.{@link Ext.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
15200 p2 = p1.{@link #ownerCt}.{@link Ext.Container#getComponent getComponent}('p2'); // reference via a sibling
15201      * </code></pre>
15202      * <p>Also see <tt>{@link #id}</tt> and <code>{@link #ref}</code>.</p>
15203      * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
15204      */
15205     /**
15206      * @cfg {String} xtype
15207      * The registered <tt>xtype</tt> to create. This config option is not used when passing
15208      * a config object into a constructor. This config option is used only when
15209      * lazy instantiation is being used, and a child item of a Container is being
15210      * specified not as a fully instantiated Component, but as a <i>Component config
15211      * object</i>. The <tt>xtype</tt> will be looked up at render time up to determine what
15212      * type of child Component to create.<br><br>
15213      * The predefined xtypes are listed {@link Ext.Component here}.
15214      * <br><br>
15215      * If you subclass Components to create your own Components, you may register
15216      * them using {@link Ext.ComponentMgr#registerType} in order to be able to
15217      * take advantage of lazy instantiation and rendering.
15218      */
15219     /**
15220      * @cfg {String} ptype
15221      * The registered <tt>ptype</tt> to create. This config option is not used when passing
15222      * a config object into a constructor. This config option is used only when
15223      * lazy instantiation is being used, and a Plugin is being
15224      * specified not as a fully instantiated Component, but as a <i>Component config
15225      * object</i>. The <tt>ptype</tt> will be looked up at render time up to determine what
15226      * type of Plugin to create.<br><br>
15227      * If you create your own Plugins, you may register them using
15228      * {@link Ext.ComponentMgr#registerPlugin} in order to be able to
15229      * take advantage of lazy instantiation and rendering.
15230      */
15231     /**
15232      * @cfg {String} cls
15233      * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
15234      * useful for adding customized styles to the component or any of its children using standard CSS rules.
15235      */
15236     /**
15237      * @cfg {String} overCls
15238      * An optional extra CSS class that will be added to this component's Element when the mouse moves
15239      * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
15240      * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
15241      */
15242     /**
15243      * @cfg {String} style
15244      * A custom style specification to be applied to this component's Element.  Should be a valid argument to
15245      * {@link Ext.Element#applyStyles}.
15246      * <pre><code>
15247 new Ext.Panel({
15248     title: 'Some Title',
15249     renderTo: Ext.getBody(),
15250     width: 400, height: 300,
15251     layout: 'form',
15252     items: [{
15253         xtype: 'textarea',
15254         style: {
15255             width: '95%',
15256             marginBottom: '10px'
15257         }
15258     },
15259         new Ext.Button({
15260             text: 'Send',
15261             minWidth: '100',
15262             style: {
15263                 marginBottom: '10px'
15264             }
15265         })
15266     ]
15267 });
15268      * </code></pre>
15269      */
15270     /**
15271      * @cfg {String} ctCls
15272      * <p>An optional extra CSS class that will be added to this component's container. This can be useful for
15273      * adding customized styles to the container or any of its children using standard CSS rules.  See
15274      * {@link Ext.layout.ContainerLayout}.{@link Ext.layout.ContainerLayout#extraCls extraCls} also.</p>
15275      * <p><b>Note</b>: <tt>ctCls</tt> defaults to <tt>''</tt> except for the following class
15276      * which assigns a value by default:
15277      * <div class="mdetail-params"><ul>
15278      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-layout-ct'</tt></li>
15279      * </ul></div>
15280      * To configure the above Class with an extra CSS class append to the default.  For example,
15281      * for BoxLayout (Hbox and Vbox):<pre><code>
15282      * ctCls: 'x-box-layout-ct custom-class'
15283      * </code></pre>
15284      * </p>
15285      */
15286     /**
15287      * @cfg {Boolean} disabled
15288      * Render this component disabled (default is false).
15289      */
15290     disabled : false,
15291     /**
15292      * @cfg {Boolean} hidden
15293      * Render this component hidden (default is false). If <tt>true</tt>, the
15294      * {@link #hide} method will be called internally.
15295      */
15296     hidden : false,
15297     /**
15298      * @cfg {Object/Array} plugins
15299      * An object or array of objects that will provide custom functionality for this component.  The only
15300      * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
15301      * When a component is created, if any plugins are available, the component will call the init method on each
15302      * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
15303      * component as needed to provide its functionality.
15304      */
15305     /**
15306      * @cfg {Mixed} applyTo
15307      * <p>Specify the id of the element, a DOM element or an existing Element corresponding to a DIV
15308      * that is already present in the document that specifies some structural markup for this
15309      * component.</p><div><ul>
15310      * <li><b>Description</b> : <ul>
15311      * <div class="sub-desc">When <tt>applyTo</tt> is used, constituent parts of the component can also be specified
15312      * by id or CSS class name within the main element, and the component being created may attempt
15313      * to create its subcomponents from that markup if applicable.</div>
15314      * </ul></li>
15315      * <li><b>Notes</b> : <ul>
15316      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
15317      * <div class="sub-desc">If applyTo is specified, any value passed for {@link #renderTo} will be ignored and the target
15318      * element's parent node will automatically be used as the component's container.</div>
15319      * </ul></li>
15320      * </ul></div>
15321      */
15322     /**
15323      * @cfg {Mixed} renderTo
15324      * <p>Specify the id of the element, a DOM element or an existing Element that this component
15325      * will be rendered into.</p><div><ul>
15326      * <li><b>Notes</b> : <ul>
15327      * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
15328      * a {@link Ext.Container Container}. It is the responsibility of the
15329      * {@link Ext.Container Container}'s {@link Ext.Container#layout layout manager}
15330      * to render and manage its child items.</div>
15331      * <div class="sub-desc">When using this config, a call to render() is not required.</div>
15332      * </ul></li>
15333      * </ul></div>
15334      * <p>See <tt>{@link #render}</tt> also.</p>
15335      */
15336     /**
15337      * @cfg {Boolean} stateful
15338      * <p>A flag which causes the Component to attempt to restore the state of
15339      * internal properties from a saved state on startup. The component must have
15340      * either a <code>{@link #stateId}</code> or <code>{@link #id}</code> assigned
15341      * for state to be managed. Auto-generated ids are not guaranteed to be stable
15342      * across page loads and cannot be relied upon to save and restore the same
15343      * state for a component.<p>
15344      * <p>For state saving to work, the state manager's provider must have been
15345      * set to an implementation of {@link Ext.state.Provider} which overrides the
15346      * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
15347      * methods to save and recall name/value pairs. A built-in implementation,
15348      * {@link Ext.state.CookieProvider} is available.</p>
15349      * <p>To set the state provider for the current page:</p>
15350      * <pre><code>
15351 Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
15352     expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
15353 }));
15354      * </code></pre>
15355      * <p>A stateful Component attempts to save state when one of the events
15356      * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
15357      * <p>To save state, a stateful Component first serializes its state by
15358      * calling <b><code>getState</code></b>. By default, this function does
15359      * nothing. The developer must provide an implementation which returns an
15360      * object hash which represents the Component's restorable state.</p>
15361      * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
15362      * which uses the configured {@link Ext.state.Provider} to save the object
15363      * keyed by the Component's <code>{@link stateId}</code>, or, if that is not
15364      * specified, its <code>{@link #id}</code>.</p>
15365      * <p>During construction, a stateful Component attempts to <i>restore</i>
15366      * its state by calling {@link Ext.state.Manager#get} passing the
15367      * <code>{@link #stateId}</code>, or, if that is not specified, the
15368      * <code>{@link #id}</code>.</p>
15369      * <p>The resulting object is passed to <b><code>applyState</code></b>.
15370      * The default implementation of <code>applyState</code> simply copies
15371      * properties into the object, but a developer may override this to support
15372      * more behaviour.</p>
15373      * <p>You can perform extra processing on state save and restore by attaching
15374      * handlers to the {@link #beforestaterestore}, {@link #staterestore},
15375      * {@link #beforestatesave} and {@link #statesave} events.</p>
15376      */
15377     /**
15378      * @cfg {String} stateId
15379      * The unique id for this component to use for state management purposes
15380      * (defaults to the component id if one was set, otherwise null if the
15381      * component is using a generated id).
15382      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
15383      * restoring Component state.</p>
15384      */
15385     /**
15386      * @cfg {Array} stateEvents
15387      * <p>An array of events that, when fired, should trigger this component to
15388      * save its state (defaults to none). <code>stateEvents</code> may be any type
15389      * of event supported by this component, including browser or custom events
15390      * (e.g., <tt>['click', 'customerchange']</tt>).</p>
15391      * <p>See <code>{@link #stateful}</code> for an explanation of saving and
15392      * restoring Component state.</p>
15393      */
15394     /**
15395      * @cfg {Mixed} autoEl
15396      * <p>A tag name or {@link Ext.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
15397      * encapsulate this Component.</p>
15398      * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}, {@link Ext.BoxComponent},
15399      * and {@link Ext.Container}, this defaults to <b><tt>'div'</tt></b>. The more complex Ext classes use a more complex
15400      * DOM structure created by their own onRender methods.</p>
15401      * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
15402      * different DOM elements. Example usage:</p><pre><code>
15403 {
15404     xtype: 'box',
15405     autoEl: {
15406         tag: 'img',
15407         src: 'http://www.example.com/example.jpg'
15408     }
15409 }, {
15410     xtype: 'box',
15411     autoEl: {
15412         tag: 'blockquote',
15413         html: 'autoEl is cool!'
15414     }
15415 }, {
15416     xtype: 'container',
15417     autoEl: 'ul',
15418     cls: 'ux-unordered-list',
15419     items: {
15420         xtype: 'box',
15421         autoEl: 'li',
15422         html: 'First list item'
15423     }
15424 }
15425 </code></pre>
15426      */
15427     autoEl : 'div',
15428
15429     /**
15430      * @cfg {String} disabledClass
15431      * CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
15432      */
15433     disabledClass : 'x-item-disabled',
15434     /**
15435      * @cfg {Boolean} allowDomMove
15436      * Whether the component can move the Dom node when rendering (defaults to true).
15437      */
15438     allowDomMove : true,
15439     /**
15440      * @cfg {Boolean} autoShow
15441      * True if the component should check for hidden classes (e.g. 'x-hidden' or 'x-hide-display') and remove
15442      * them on render (defaults to false).
15443      */
15444     autoShow : false,
15445     /**
15446      * @cfg {String} hideMode
15447      * <p>How this component should be hidden. Supported values are <tt>'visibility'</tt>
15448      * (css visibility), <tt>'offsets'</tt> (negative offset position) and <tt>'display'</tt>
15449      * (css display).</p>
15450      * <br><p><b>Note</b>: the default of <tt>'display'</tt> is generally preferred
15451      * since items are automatically laid out when they are first shown (no sizing
15452      * is done while hidden).</p>
15453      */
15454     hideMode : 'display',
15455     /**
15456      * @cfg {Boolean} hideParent
15457      * True to hide and show the component's container when hide/show is called on the component, false to hide
15458      * and show the component itself (defaults to false).  For example, this can be used as a shortcut for a hide
15459      * button on a window by setting hide:true on the button when adding it to its parent container.
15460      */
15461     hideParent : false,
15462     /**
15463      * <p>The {@link Ext.Element} which encapsulates this Component. Read-only.</p>
15464      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
15465      * that may be overridden using the <code>{@link #autoEl}</code> config.</p>
15466      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
15467      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
15468      * for this Component's own Observable events), see the {@link Ext.util.Observable#listeners listeners}
15469      * config for a suggestion, or use a render listener directly:</p><pre><code>
15470 new Ext.Panel({
15471     title: 'The Clickable Panel',
15472     listeners: {
15473         render: function(p) {
15474             // Append the Panel to the click handler&#39;s argument list.
15475             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
15476         },
15477         single: true  // Remove the listener after first invocation
15478     }
15479 });
15480 </code></pre>
15481      * <p>See also <tt>{@link #getEl getEl}</p>
15482      * @type Ext.Element
15483      * @property el
15484      */
15485     /**
15486      * This Component's owner {@link Ext.Container Container} (defaults to undefined, and is set automatically when
15487      * this Component is added to a Container).  Read-only.
15488      * <p><b>Note</b>: to access items within the Container see <tt>{@link #itemId}</tt>.</p>
15489      * @type Ext.Container
15490      * @property ownerCt
15491      */
15492     /**
15493      * True if this component is hidden. Read-only.
15494      * @type Boolean
15495      * @property hidden
15496      */
15497     /**
15498      * True if this component is disabled. Read-only.
15499      * @type Boolean
15500      * @property disabled
15501      */
15502     /**
15503      * True if this component has been rendered. Read-only.
15504      * @type Boolean
15505      * @property rendered
15506      */
15507     rendered : false,
15508
15509     /**
15510      * @cfg {String} contentEl
15511      * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as the content
15512      * for this component.</p>
15513      * <ul>
15514      * <li><b>Description</b> :
15515      * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the layout element
15516      * 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>
15517      * <li><b>Notes</b> :
15518      * <div class="sub-desc">The specified HTML element is appended to the layout element of the component <i>after any configured
15519      * {@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>
15520      * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.Container#layout layout}</b></code>
15521      * scheme that the Component may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.Container#items items}</b></code>.</div>
15522      * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
15523      * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
15524      * </ul>
15525      */
15526     /**
15527      * @cfg {String/Object} html
15528      * An HTML fragment, or a {@link Ext.DomHelper DomHelper} specification to use as the layout element
15529      * content (defaults to ''). The HTML content is added after the component is rendered,
15530      * so the document will not contain this HTML at the time the {@link #render} event is fired.
15531      * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
15532      */
15533
15534     /**
15535      * @cfg {Mixed} tpl
15536      * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
15537      * or an array of strings to form an Ext.XTemplate.
15538      * Used in conjunction with the <code>{@link #data}</code> and
15539      * <code>{@link #tplWriteMode}</code> configurations.
15540      */
15541
15542     /**
15543      * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
15544      * updating the content area of the Component. Defaults to <tt>'overwrite'</tt>
15545      * (see <code>{@link Ext.XTemplate#overwrite}</code>).
15546      */
15547     tplWriteMode : 'overwrite',
15548
15549     /**
15550      * @cfg {Mixed} data
15551      * The initial set of data to apply to the <code>{@link #tpl}</code> to
15552      * update the content area of the Component.
15553      */
15554     
15555     /**
15556      * @cfg {Array} bubbleEvents
15557      * <p>An array of events that, when fired, should be bubbled to any parent container.
15558      * See {@link Ext.util.Observable#enableBubble}.
15559      * Defaults to <tt>[]</tt>.
15560      */
15561     bubbleEvents: [],
15562
15563
15564     // private
15565     ctype : 'Ext.Component',
15566
15567     // private
15568     actionMode : 'el',
15569
15570     // private
15571     getActionEl : function(){
15572         return this[this.actionMode];
15573     },
15574
15575     initPlugin : function(p){
15576         if(p.ptype && !Ext.isFunction(p.init)){
15577             p = Ext.ComponentMgr.createPlugin(p);
15578         }else if(Ext.isString(p)){
15579             p = Ext.ComponentMgr.createPlugin({
15580                 ptype: p
15581             });
15582         }
15583         p.init(this);
15584         return p;
15585     },
15586
15587     /* // protected
15588      * Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
15589      * <pre><code>
15590 // Traditional constructor:
15591 Ext.Foo = function(config){
15592     // call superclass constructor:
15593     Ext.Foo.superclass.constructor.call(this, config);
15594
15595     this.addEvents({
15596         // add events
15597     });
15598 };
15599 Ext.extend(Ext.Foo, Ext.Bar, {
15600    // class body
15601 }
15602
15603 // initComponent replaces the constructor:
15604 Ext.Foo = Ext.extend(Ext.Bar, {
15605     initComponent : function(){
15606         // call superclass initComponent
15607         Ext.Container.superclass.initComponent.call(this);
15608
15609         this.addEvents({
15610             // add events
15611         });
15612     }
15613 }
15614 </code></pre>
15615      */
15616     initComponent : function(){
15617         /*
15618          * this is double processing, however it allows people to be able to do
15619          * Ext.apply(this, {
15620          *     listeners: {
15621          *         //here
15622          *     }
15623          * });
15624          * MyClass.superclass.initComponent.call(this);
15625          */
15626         if(this.listeners){
15627             this.on(this.listeners);
15628             delete this.listeners;
15629         }
15630         this.enableBubble(this.bubbleEvents);
15631     },
15632
15633     /**
15634      * <p>Render this Component into the passed HTML element.</p>
15635      * <p><b>If you are using a {@link Ext.Container Container} object to house this Component, then
15636      * do not use the render method.</b></p>
15637      * <p>A Container's child Components are rendered by that Container's
15638      * {@link Ext.Container#layout layout} manager when the Container is first rendered.</p>
15639      * <p>Certain layout managers allow dynamic addition of child components. Those that do
15640      * include {@link Ext.layout.CardLayout}, {@link Ext.layout.AnchorLayout},
15641      * {@link Ext.layout.FormLayout}, {@link Ext.layout.TableLayout}.</p>
15642      * <p>If the Container is already rendered when a new child Component is added, you may need to call
15643      * the Container's {@link Ext.Container#doLayout doLayout} to refresh the view which causes any
15644      * unrendered child Components to be rendered. This is required so that you can add multiple
15645      * child components if needed while only refreshing the layout once.</p>
15646      * <p>When creating complex UIs, it is important to remember that sizing and positioning
15647      * of child items is the responsibility of the Container's {@link Ext.Container#layout layout} manager.
15648      * If you expect child items to be sized in response to user interactions, you must
15649      * configure the Container with a layout manager which creates and manages the type of layout you
15650      * have in mind.</p>
15651      * <p><b>Omitting the Container's {@link Ext.Container#layout layout} config means that a basic
15652      * layout manager is used which does nothing but render child components sequentially into the
15653      * Container. No sizing or positioning will be performed in this situation.</b></p>
15654      * @param {Element/HTMLElement/String} container (optional) The element this Component should be
15655      * rendered into. If it is being created from existing markup, this should be omitted.
15656      * @param {String/Number} position (optional) The element ID or DOM node index within the container <b>before</b>
15657      * which this component will be inserted (defaults to appending to the end of the container)
15658      */
15659     render : function(container, position){
15660         if(!this.rendered && this.fireEvent('beforerender', this) !== false){
15661             if(!container && this.el){
15662                 this.el = Ext.get(this.el);
15663                 container = this.el.dom.parentNode;
15664                 this.allowDomMove = false;
15665             }
15666             this.container = Ext.get(container);
15667             if(this.ctCls){
15668                 this.container.addClass(this.ctCls);
15669             }
15670             this.rendered = true;
15671             if(position !== undefined){
15672                 if(Ext.isNumber(position)){
15673                     position = this.container.dom.childNodes[position];
15674                 }else{
15675                     position = Ext.getDom(position);
15676                 }
15677             }
15678             this.onRender(this.container, position || null);
15679             if(this.autoShow){
15680                 this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
15681             }
15682             if(this.cls){
15683                 this.el.addClass(this.cls);
15684                 delete this.cls;
15685             }
15686             if(this.style){
15687                 this.el.applyStyles(this.style);
15688                 delete this.style;
15689             }
15690             if(this.overCls){
15691                 this.el.addClassOnOver(this.overCls);
15692             }
15693             this.fireEvent('render', this);
15694
15695
15696             // Populate content of the component with html, contentEl or
15697             // a tpl.
15698             var contentTarget = this.getContentTarget();
15699             if (this.html){
15700                 contentTarget.update(Ext.DomHelper.markup(this.html));
15701                 delete this.html;
15702             }
15703             if (this.contentEl){
15704                 var ce = Ext.getDom(this.contentEl);
15705                 Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
15706                 contentTarget.appendChild(ce);
15707             }
15708             if (this.tpl) {
15709                 if (!this.tpl.compile) {
15710                     this.tpl = new Ext.XTemplate(this.tpl);
15711                 }
15712                 if (this.data) {
15713                     this.tpl[this.tplWriteMode](contentTarget, this.data);
15714                     delete this.data;
15715                 }
15716             }
15717             this.afterRender(this.container);
15718
15719
15720             if(this.hidden){
15721                 // call this so we don't fire initial hide events.
15722                 this.doHide();
15723             }
15724             if(this.disabled){
15725                 // pass silent so the event doesn't fire the first time.
15726                 this.disable(true);
15727             }
15728
15729             if(this.stateful !== false){
15730                 this.initStateEvents();
15731             }
15732             this.fireEvent('afterrender', this);
15733         }
15734         return this;
15735     },
15736
15737
15738     /**
15739      * Update the content area of a component.
15740      * @param {Mixed} htmlOrData
15741      * If this component has been configured with a template via the tpl config
15742      * then it will use this argument as data to populate the template.
15743      * If this component was not configured with a template, the components
15744      * content area will be updated via Ext.Element update
15745      * @param {Boolean} loadScripts
15746      * (optional) Only legitimate when using the html configuration. Defaults to false
15747      * @param {Function} callback
15748      * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
15749      */
15750     update: function(htmlOrData, loadScripts, cb) {
15751         var contentTarget = this.getContentTarget();
15752         if (this.tpl && typeof htmlOrData !== "string") {
15753             this.tpl[this.tplWriteMode](contentTarget, htmlOrData || {});
15754         } else {
15755             var html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
15756             contentTarget.update(html, loadScripts, cb);
15757         }
15758     },
15759
15760
15761     /**
15762      * @private
15763      * Method to manage awareness of when components are added to their
15764      * respective Container, firing an added event.
15765      * References are established at add time rather than at render time.
15766      * @param {Ext.Container} container Container which holds the component
15767      * @param {number} pos Position at which the component was added
15768      */
15769     onAdded : function(container, pos) {
15770         this.ownerCt = container;
15771         this.initRef();
15772         this.fireEvent('added', this, container, pos);
15773     },
15774
15775     /**
15776      * @private
15777      * Method to manage awareness of when components are removed from their
15778      * respective Container, firing an removed event. References are properly
15779      * cleaned up after removing a component from its owning container.
15780      */
15781     onRemoved : function() {
15782         this.removeRef();
15783         this.fireEvent('removed', this, this.ownerCt);
15784         delete this.ownerCt;
15785     },
15786
15787     /**
15788      * @private
15789      * Method to establish a reference to a component.
15790      */
15791     initRef : function() {
15792         /**
15793          * @cfg {String} ref
15794          * <p>A path specification, relative to the Component's <code>{@link #ownerCt}</code>
15795          * specifying into which ancestor Container to place a named reference to this Component.</p>
15796          * <p>The ancestor axis can be traversed by using '/' characters in the path.
15797          * For example, to put a reference to a Toolbar Button into <i>the Panel which owns the Toolbar</i>:</p><pre><code>
15798 var myGrid = new Ext.grid.EditorGridPanel({
15799     title: 'My EditorGridPanel',
15800     store: myStore,
15801     colModel: myColModel,
15802     tbar: [{
15803         text: 'Save',
15804         handler: saveChanges,
15805         disabled: true,
15806         ref: '../saveButton'
15807     }],
15808     listeners: {
15809         afteredit: function() {
15810 //          The button reference is in the GridPanel
15811             myGrid.saveButton.enable();
15812         }
15813     }
15814 });
15815 </code></pre>
15816          * <p>In the code above, if the <code>ref</code> had been <code>'saveButton'</code>
15817          * the reference would have been placed into the Toolbar. Each '/' in the <code>ref</code>
15818          * moves up one level from the Component's <code>{@link #ownerCt}</code>.</p>
15819          * <p>Also see the <code>{@link #added}</code> and <code>{@link #removed}</code> events.</p>
15820          */
15821         if(this.ref && !this.refOwner){
15822             var levels = this.ref.split('/'),
15823                 last = levels.length,
15824                 i = 0,
15825                 t = this;
15826
15827             while(t && i < last){
15828                 t = t.ownerCt;
15829                 ++i;
15830             }
15831             if(t){
15832                 t[this.refName = levels[--i]] = this;
15833                 /**
15834                  * @type Ext.Container
15835                  * @property refOwner
15836                  * The ancestor Container into which the {@link #ref} reference was inserted if this Component
15837                  * is a child of a Container, and has been configured with a <code>ref</code>.
15838                  */
15839                 this.refOwner = t;
15840             }
15841         }
15842     },
15843
15844     removeRef : function() {
15845         if (this.refOwner && this.refName) {
15846             delete this.refOwner[this.refName];
15847             delete this.refOwner;
15848         }
15849     },
15850
15851     // private
15852     initState : function(){
15853         if(Ext.state.Manager){
15854             var id = this.getStateId();
15855             if(id){
15856                 var state = Ext.state.Manager.get(id);
15857                 if(state){
15858                     if(this.fireEvent('beforestaterestore', this, state) !== false){
15859                         this.applyState(Ext.apply({}, state));
15860                         this.fireEvent('staterestore', this, state);
15861                     }
15862                 }
15863             }
15864         }
15865     },
15866
15867     // private
15868     getStateId : function(){
15869         return this.stateId || ((/^(ext-comp-|ext-gen)/).test(String(this.id)) ? null : this.id);
15870     },
15871
15872     // private
15873     initStateEvents : function(){
15874         if(this.stateEvents){
15875             for(var i = 0, e; e = this.stateEvents[i]; i++){
15876                 this.on(e, this.saveState, this, {delay:100});
15877             }
15878         }
15879     },
15880
15881     // private
15882     applyState : function(state){
15883         if(state){
15884             Ext.apply(this, state);
15885         }
15886     },
15887
15888     // private
15889     getState : function(){
15890         return null;
15891     },
15892
15893     // private
15894     saveState : function(){
15895         if(Ext.state.Manager && this.stateful !== false){
15896             var id = this.getStateId();
15897             if(id){
15898                 var state = this.getState();
15899                 if(this.fireEvent('beforestatesave', this, state) !== false){
15900                     Ext.state.Manager.set(id, state);
15901                     this.fireEvent('statesave', this, state);
15902                 }
15903             }
15904         }
15905     },
15906
15907     /**
15908      * Apply this component to existing markup that is valid. With this function, no call to render() is required.
15909      * @param {String/HTMLElement} el
15910      */
15911     applyToMarkup : function(el){
15912         this.allowDomMove = false;
15913         this.el = Ext.get(el);
15914         this.render(this.el.dom.parentNode);
15915     },
15916
15917     /**
15918      * Adds a CSS class to the component's underlying element.
15919      * @param {string} cls The CSS class name to add
15920      * @return {Ext.Component} this
15921      */
15922     addClass : function(cls){
15923         if(this.el){
15924             this.el.addClass(cls);
15925         }else{
15926             this.cls = this.cls ? this.cls + ' ' + cls : cls;
15927         }
15928         return this;
15929     },
15930
15931     /**
15932      * Removes a CSS class from the component's underlying element.
15933      * @param {string} cls The CSS class name to remove
15934      * @return {Ext.Component} this
15935      */
15936     removeClass : function(cls){
15937         if(this.el){
15938             this.el.removeClass(cls);
15939         }else if(this.cls){
15940             this.cls = this.cls.split(' ').remove(cls).join(' ');
15941         }
15942         return this;
15943     },
15944
15945     // private
15946     // default function is not really useful
15947     onRender : function(ct, position){
15948         if(!this.el && this.autoEl){
15949             if(Ext.isString(this.autoEl)){
15950                 this.el = document.createElement(this.autoEl);
15951             }else{
15952                 var div = document.createElement('div');
15953                 Ext.DomHelper.overwrite(div, this.autoEl);
15954                 this.el = div.firstChild;
15955             }
15956             if (!this.el.id) {
15957                 this.el.id = this.getId();
15958             }
15959         }
15960         if(this.el){
15961             this.el = Ext.get(this.el);
15962             if(this.allowDomMove !== false){
15963                 ct.dom.insertBefore(this.el.dom, position);
15964                 if (div) {
15965                     Ext.removeNode(div);
15966                     div = null;
15967                 }
15968             }
15969         }
15970     },
15971
15972     // private
15973     getAutoCreate : function(){
15974         var cfg = Ext.isObject(this.autoCreate) ?
15975                       this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
15976         if(this.id && !cfg.id){
15977             cfg.id = this.id;
15978         }
15979         return cfg;
15980     },
15981
15982     // private
15983     afterRender : Ext.emptyFn,
15984
15985     /**
15986      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15987      * removing the component from its {@link Ext.Container} (if applicable) and unregistering it from
15988      * {@link Ext.ComponentMgr}.  Destruction is generally handled automatically by the framework and this method
15989      * should usually not need to be called directly.
15990      *
15991      */
15992     destroy : function(){
15993         if(!this.isDestroyed){
15994             if(this.fireEvent('beforedestroy', this) !== false){
15995                 this.destroying = true;
15996                 this.beforeDestroy();
15997                 if(this.ownerCt && this.ownerCt.remove){
15998                     this.ownerCt.remove(this, false);
15999                 }
16000                 if(this.rendered){
16001                     this.el.remove();
16002                     if(this.actionMode == 'container' || this.removeMode == 'container'){
16003                         this.container.remove();
16004                     }
16005                 }
16006                 // Stop any buffered tasks
16007                 if(this.focusTask && this.focusTask.cancel){
16008                     this.focusTask.cancel();
16009                 }
16010                 this.onDestroy();
16011                 Ext.ComponentMgr.unregister(this);
16012                 this.fireEvent('destroy', this);
16013                 this.purgeListeners();
16014                 this.destroying = false;
16015                 this.isDestroyed = true;
16016             }
16017         }
16018     },
16019
16020     deleteMembers : function(){
16021         var args = arguments;
16022         for(var i = 0, len = args.length; i < len; ++i){
16023             delete this[args[i]];
16024         }
16025     },
16026
16027     // private
16028     beforeDestroy : Ext.emptyFn,
16029
16030     // private
16031     onDestroy  : Ext.emptyFn,
16032
16033     /**
16034      * <p>Returns the {@link Ext.Element} which encapsulates this Component.</p>
16035      * <p>This will <i>usually</i> be a &lt;DIV> element created by the class's onRender method, but
16036      * that may be overridden using the {@link #autoEl} config.</p>
16037      * <br><p><b>Note</b>: this element will not be available until this Component has been rendered.</p><br>
16038      * <p>To add listeners for <b>DOM events</b> to this Component (as opposed to listeners
16039      * for this Component's own Observable events), see the {@link #listeners} config for a suggestion,
16040      * or use a render listener directly:</p><pre><code>
16041 new Ext.Panel({
16042     title: 'The Clickable Panel',
16043     listeners: {
16044         render: function(p) {
16045             // Append the Panel to the click handler&#39;s argument list.
16046             p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
16047         },
16048         single: true  // Remove the listener after first invocation
16049     }
16050 });
16051 </code></pre>
16052      * @return {Ext.Element} The Element which encapsulates this Component.
16053      */
16054     getEl : function(){
16055         return this.el;
16056     },
16057
16058     // private
16059     getContentTarget : function(){
16060         return this.el;
16061     },
16062
16063     /**
16064      * Returns the <code>id</code> of this component or automatically generates and
16065      * returns an <code>id</code> if an <code>id</code> is not defined yet:<pre><code>
16066      * 'ext-comp-' + (++Ext.Component.AUTO_ID)
16067      * </code></pre>
16068      * @return {String} id
16069      */
16070     getId : function(){
16071         return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
16072     },
16073
16074     /**
16075      * Returns the <code>{@link #itemId}</code> of this component.  If an
16076      * <code>{@link #itemId}</code> was not assigned through configuration the
16077      * <code>id</code> is returned using <code>{@link #getId}</code>.
16078      * @return {String}
16079      */
16080     getItemId : function(){
16081         return this.itemId || this.getId();
16082     },
16083
16084     /**
16085      * Try to focus this component.
16086      * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
16087      * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
16088      * @return {Ext.Component} this
16089      */
16090     focus : function(selectText, delay){
16091         if(delay){
16092             this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]);
16093             this.focusTask.delay(Ext.isNumber(delay) ? delay : 10);
16094             return;
16095         }
16096         if(this.rendered && !this.isDestroyed){
16097             this.el.focus();
16098             if(selectText === true){
16099                 this.el.dom.select();
16100             }
16101         }
16102         return this;
16103     },
16104
16105     // private
16106     blur : function(){
16107         if(this.rendered){
16108             this.el.blur();
16109         }
16110         return this;
16111     },
16112
16113     /**
16114      * Disable this component and fire the 'disable' event.
16115      * @return {Ext.Component} this
16116      */
16117     disable : function(/* private */ silent){
16118         if(this.rendered){
16119             this.onDisable();
16120         }
16121         this.disabled = true;
16122         if(silent !== true){
16123             this.fireEvent('disable', this);
16124         }
16125         return this;
16126     },
16127
16128     // private
16129     onDisable : function(){
16130         this.getActionEl().addClass(this.disabledClass);
16131         this.el.dom.disabled = true;
16132     },
16133
16134     /**
16135      * Enable this component and fire the 'enable' event.
16136      * @return {Ext.Component} this
16137      */
16138     enable : function(){
16139         if(this.rendered){
16140             this.onEnable();
16141         }
16142         this.disabled = false;
16143         this.fireEvent('enable', this);
16144         return this;
16145     },
16146
16147     // private
16148     onEnable : function(){
16149         this.getActionEl().removeClass(this.disabledClass);
16150         this.el.dom.disabled = false;
16151     },
16152
16153     /**
16154      * Convenience function for setting disabled/enabled by boolean.
16155      * @param {Boolean} disabled
16156      * @return {Ext.Component} this
16157      */
16158     setDisabled : function(disabled){
16159         return this[disabled ? 'disable' : 'enable']();
16160     },
16161
16162     /**
16163      * Show this component.  Listen to the '{@link #beforeshow}' event and return
16164      * <tt>false</tt> to cancel showing the component.  Fires the '{@link #show}'
16165      * event after showing the component.
16166      * @return {Ext.Component} this
16167      */
16168     show : function(){
16169         if(this.fireEvent('beforeshow', this) !== false){
16170             this.hidden = false;
16171             if(this.autoRender){
16172                 this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
16173             }
16174             if(this.rendered){
16175                 this.onShow();
16176             }
16177             this.fireEvent('show', this);
16178         }
16179         return this;
16180     },
16181
16182     // private
16183     onShow : function(){
16184         this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
16185     },
16186
16187     /**
16188      * Hide this component.  Listen to the '{@link #beforehide}' event and return
16189      * <tt>false</tt> to cancel hiding the component.  Fires the '{@link #hide}'
16190      * event after hiding the component. Note this method is called internally if
16191      * the component is configured to be <code>{@link #hidden}</code>.
16192      * @return {Ext.Component} this
16193      */
16194     hide : function(){
16195         if(this.fireEvent('beforehide', this) !== false){
16196             this.doHide();
16197             this.fireEvent('hide', this);
16198         }
16199         return this;
16200     },
16201
16202     // private
16203     doHide: function(){
16204         this.hidden = true;
16205         if(this.rendered){
16206             this.onHide();
16207         }
16208     },
16209
16210     // private
16211     onHide : function(){
16212         this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
16213     },
16214
16215     // private
16216     getVisibilityEl : function(){
16217         return this.hideParent ? this.container : this.getActionEl();
16218     },
16219
16220     /**
16221      * Convenience function to hide or show this component by boolean.
16222      * @param {Boolean} visible True to show, false to hide
16223      * @return {Ext.Component} this
16224      */
16225     setVisible : function(visible){
16226         return this[visible ? 'show' : 'hide']();
16227     },
16228
16229     /**
16230      * Returns true if this component is visible.
16231      * @return {Boolean} True if this component is visible, false otherwise.
16232      */
16233     isVisible : function(){
16234         return this.rendered && this.getVisibilityEl().isVisible();
16235     },
16236
16237     /**
16238      * Clone the current component using the original config values passed into this instance by default.
16239      * @param {Object} overrides A new config containing any properties to override in the cloned version.
16240      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
16241      * @return {Ext.Component} clone The cloned copy of this component
16242      */
16243     cloneConfig : function(overrides){
16244         overrides = overrides || {};
16245         var id = overrides.id || Ext.id();
16246         var cfg = Ext.applyIf(overrides, this.initialConfig);
16247         cfg.id = id; // prevent dup id
16248         return new this.constructor(cfg);
16249     },
16250
16251     /**
16252      * Gets the xtype for this component as registered with {@link Ext.ComponentMgr}. For a list of all
16253      * available xtypes, see the {@link Ext.Component} header. Example usage:
16254      * <pre><code>
16255 var t = new Ext.form.TextField();
16256 alert(t.getXType());  // alerts 'textfield'
16257 </code></pre>
16258      * @return {String} The xtype
16259      */
16260     getXType : function(){
16261         return this.constructor.xtype;
16262     },
16263
16264     /**
16265      * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
16266      * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
16267      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
16268      * to participate in determination of inherited xtypes.</b></p>
16269      * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
16270      * <p>Example usage:</p>
16271      * <pre><code>
16272 var t = new Ext.form.TextField();
16273 var isText = t.isXType('textfield');        // true
16274 var isBoxSubclass = t.isXType('box');       // true, descended from BoxComponent
16275 var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
16276 </code></pre>
16277      * @param {String} xtype The xtype to check for this Component
16278      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
16279      * the default), or true to check whether this Component is directly of the specified xtype.
16280      * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
16281      */
16282     isXType : function(xtype, shallow){
16283         //assume a string by default
16284         if (Ext.isFunction(xtype)){
16285             xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
16286         }else if (Ext.isObject(xtype)){
16287             xtype = xtype.constructor.xtype; //handle being passed an instance
16288         }
16289
16290         return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
16291     },
16292
16293     /**
16294      * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
16295      * available xtypes, see the {@link Ext.Component} header.</p>
16296      * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
16297      * to participate in determination of inherited xtypes.</b></p>
16298      * <p>Example usage:</p>
16299      * <pre><code>
16300 var t = new Ext.form.TextField();
16301 alert(t.getXTypes());  // alerts 'component/box/field/textfield'
16302 </code></pre>
16303      * @return {String} The xtype hierarchy string
16304      */
16305     getXTypes : function(){
16306         var tc = this.constructor;
16307         if(!tc.xtypes){
16308             var c = [], sc = this;
16309             while(sc && sc.constructor.xtype){
16310                 c.unshift(sc.constructor.xtype);
16311                 sc = sc.constructor.superclass;
16312             }
16313             tc.xtypeChain = c;
16314             tc.xtypes = c.join('/');
16315         }
16316         return tc.xtypes;
16317     },
16318
16319     /**
16320      * Find a container above this component at any level by a custom function. If the passed function returns
16321      * true, the container will be returned.
16322      * @param {Function} fn The custom function to call with the arguments (container, this component).
16323      * @return {Ext.Container} The first Container for which the custom function returns true
16324      */
16325     findParentBy : function(fn) {
16326         for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
16327         return p || null;
16328     },
16329
16330     /**
16331      * Find a container above this component at any level by xtype or class
16332      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
16333      * @return {Ext.Container} The first Container which matches the given xtype or class
16334      */
16335     findParentByType : function(xtype) {
16336         return Ext.isFunction(xtype) ?
16337             this.findParentBy(function(p){
16338                 return p.constructor === xtype;
16339             }) :
16340             this.findParentBy(function(p){
16341                 return p.constructor.xtype === xtype;
16342             });
16343     },
16344
16345     // protected
16346     getPositionEl : function(){
16347         return this.positionEl || this.el;
16348     },
16349
16350     // private
16351     purgeListeners : function(){
16352         Ext.Component.superclass.purgeListeners.call(this);
16353         if(this.mons){
16354             this.on('beforedestroy', this.clearMons, this, {single: true});
16355         }
16356     },
16357
16358     // private
16359     clearMons : function(){
16360         Ext.each(this.mons, function(m){
16361             m.item.un(m.ename, m.fn, m.scope);
16362         }, this);
16363         this.mons = [];
16364     },
16365
16366     // private
16367     createMons: function(){
16368         if(!this.mons){
16369             this.mons = [];
16370             this.on('beforedestroy', this.clearMons, this, {single: true});
16371         }
16372     },
16373
16374     /**
16375      * <p>Adds listeners to any Observable object (or Elements) which are automatically removed when this Component
16376      * is destroyed. Usage:</p><code><pre>
16377 myGridPanel.mon(myGridPanel.getSelectionModel(), 'selectionchange', handleSelectionChange, null, {buffer: 50});
16378 </pre></code>
16379      * <p>or:</p><code><pre>
16380 myGridPanel.mon(myGridPanel.getSelectionModel(), {
16381     selectionchange: handleSelectionChange,
16382     buffer: 50
16383 });
16384 </pre></code>
16385      * @param {Observable|Element} item The item to which to add a listener/listeners.
16386      * @param {Object|String} ename The event name, or an object containing event name properties.
16387      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
16388      * is the handler function.
16389      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
16390      * is the scope (<code>this</code> reference) in which the handler function is executed.
16391      * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this
16392      * is the {@link Ext.util.Observable#addListener addListener} options.
16393      */
16394     mon : function(item, ename, fn, scope, opt){
16395         this.createMons();
16396         if(Ext.isObject(ename)){
16397             var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
16398
16399             var o = ename;
16400             for(var e in o){
16401                 if(propRe.test(e)){
16402                     continue;
16403                 }
16404                 if(Ext.isFunction(o[e])){
16405                     // shared options
16406                     this.mons.push({
16407                         item: item, ename: e, fn: o[e], scope: o.scope
16408                     });
16409                     item.on(e, o[e], o.scope, o);
16410                 }else{
16411                     // individual options
16412                     this.mons.push({
16413                         item: item, ename: e, fn: o[e], scope: o.scope
16414                     });
16415                     item.on(e, o[e]);
16416                 }
16417             }
16418             return;
16419         }
16420
16421         this.mons.push({
16422             item: item, ename: ename, fn: fn, scope: scope
16423         });
16424         item.on(ename, fn, scope, opt);
16425     },
16426
16427     /**
16428      * Removes listeners that were added by the {@link #mon} method.
16429      * @param {Observable|Element} item The item from which to remove a listener/listeners.
16430      * @param {Object|String} ename The event name, or an object containing event name properties.
16431      * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
16432      * is the handler function.
16433      * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
16434      * is the scope (<code>this</code> reference) in which the handler function is executed.
16435      */
16436     mun : function(item, ename, fn, scope){
16437         var found, mon;
16438         this.createMons();
16439         for(var i = 0, len = this.mons.length; i < len; ++i){
16440             mon = this.mons[i];
16441             if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
16442                 this.mons.splice(i, 1);
16443                 item.un(ename, fn, scope);
16444                 found = true;
16445                 break;
16446             }
16447         }
16448         return found;
16449     },
16450
16451     /**
16452      * Returns the next component in the owning container
16453      * @return Ext.Component
16454      */
16455     nextSibling : function(){
16456         if(this.ownerCt){
16457             var index = this.ownerCt.items.indexOf(this);
16458             if(index != -1 && index+1 < this.ownerCt.items.getCount()){
16459                 return this.ownerCt.items.itemAt(index+1);
16460             }
16461         }
16462         return null;
16463     },
16464
16465     /**
16466      * Returns the previous component in the owning container
16467      * @return Ext.Component
16468      */
16469     previousSibling : function(){
16470         if(this.ownerCt){
16471             var index = this.ownerCt.items.indexOf(this);
16472             if(index > 0){
16473                 return this.ownerCt.items.itemAt(index-1);
16474             }
16475         }
16476         return null;
16477     },
16478
16479     /**
16480      * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
16481      * @return {Ext.Container} the Container which owns this Component.
16482      */
16483     getBubbleTarget : function(){
16484         return this.ownerCt;
16485     }
16486 });
16487
16488 Ext.reg('component', Ext.Component);/**
16489  * @class Ext.Action
16490  * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it
16491  * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI
16492  * updates across any components that support the Action interface (primarily {@link Ext.Toolbar}, {@link Ext.Button}
16493  * and {@link Ext.menu.Menu} components).</p>
16494  * <p>Aside from supporting the config object interface, any component that needs to use Actions must also support
16495  * the following method list, as these will be called as needed by the Action class: setText(string), setIconCls(string),
16496  * setDisabled(boolean), setVisible(boolean) and setHandler(function).</p>
16497  * Example usage:<br>
16498  * <pre><code>
16499 // Define the shared action.  Each component below will have the same
16500 // display text and icon, and will display the same message on click.
16501 var action = new Ext.Action({
16502     {@link #text}: 'Do something',
16503     {@link #handler}: function(){
16504         Ext.Msg.alert('Click', 'You did something.');
16505     },
16506     {@link #iconCls}: 'do-something',
16507     {@link #itemId}: 'myAction'
16508 });
16509
16510 var panel = new Ext.Panel({
16511     title: 'Actions',
16512     width: 500,
16513     height: 300,
16514     tbar: [
16515         // Add the action directly to a toolbar as a menu button
16516         action,
16517         {
16518             text: 'Action Menu',
16519             // Add the action to a menu as a text item
16520             menu: [action]
16521         }
16522     ],
16523     items: [
16524         // Add the action to the panel body as a standard button
16525         new Ext.Button(action)
16526     ],
16527     renderTo: Ext.getBody()
16528 });
16529
16530 // Change the text for all components using the action
16531 action.setText('Something else');
16532
16533 // Reference an action through a container using the itemId
16534 var btn = panel.getComponent('myAction');
16535 var aRef = btn.baseAction;
16536 aRef.setText('New text');
16537 </code></pre>
16538  * @constructor
16539  * @param {Object} config The configuration options
16540  */
16541 Ext.Action = Ext.extend(Object, {
16542     /**
16543      * @cfg {String} text The text to set for all components using this action (defaults to '').
16544      */
16545     /**
16546      * @cfg {String} iconCls
16547      * The CSS class selector that specifies a background image to be used as the header icon for
16548      * all components using this action (defaults to '').
16549      * <p>An example of specifying a custom icon class would be something like:
16550      * </p><pre><code>
16551 // specify the property in the config for the class:
16552      ...
16553      iconCls: 'do-something'
16554
16555 // css class that specifies background image to be used as the icon image:
16556 .do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
16557 </code></pre>
16558      */
16559     /**
16560      * @cfg {Boolean} disabled True to disable all components using this action, false to enable them (defaults to false).
16561      */
16562     /**
16563      * @cfg {Boolean} hidden True to hide all components using this action, false to show them (defaults to false).
16564      */
16565     /**
16566      * @cfg {Function} handler The function that will be invoked by each component tied to this action
16567      * when the component's primary event is triggered (defaults to undefined).
16568      */
16569     /**
16570      * @cfg {String} itemId
16571      * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.
16572      */
16573     /**
16574      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
16575      * <code>{@link #handler}</code> is executed. Defaults to this Button.
16576      */
16577
16578     constructor : function(config){
16579         this.initialConfig = config;
16580         this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
16581         this.items = [];
16582     },
16583     
16584     // private
16585     isAction : true,
16586
16587     /**
16588      * Sets the text to be displayed by all components using this action.
16589      * @param {String} text The text to display
16590      */
16591     setText : function(text){
16592         this.initialConfig.text = text;
16593         this.callEach('setText', [text]);
16594     },
16595
16596     /**
16597      * Gets the text currently displayed by all components using this action.
16598      */
16599     getText : function(){
16600         return this.initialConfig.text;
16601     },
16602
16603     /**
16604      * Sets the icon CSS class for all components using this action.  The class should supply
16605      * a background image that will be used as the icon image.
16606      * @param {String} cls The CSS class supplying the icon image
16607      */
16608     setIconClass : function(cls){
16609         this.initialConfig.iconCls = cls;
16610         this.callEach('setIconClass', [cls]);
16611     },
16612
16613     /**
16614      * Gets the icon CSS class currently used by all components using this action.
16615      */
16616     getIconClass : function(){
16617         return this.initialConfig.iconCls;
16618     },
16619
16620     /**
16621      * Sets the disabled state of all components using this action.  Shortcut method
16622      * for {@link #enable} and {@link #disable}.
16623      * @param {Boolean} disabled True to disable the component, false to enable it
16624      */
16625     setDisabled : function(v){
16626         this.initialConfig.disabled = v;
16627         this.callEach('setDisabled', [v]);
16628     },
16629
16630     /**
16631      * Enables all components using this action.
16632      */
16633     enable : function(){
16634         this.setDisabled(false);
16635     },
16636
16637     /**
16638      * Disables all components using this action.
16639      */
16640     disable : function(){
16641         this.setDisabled(true);
16642     },
16643
16644     /**
16645      * Returns true if the components using this action are currently disabled, else returns false.  
16646      */
16647     isDisabled : function(){
16648         return this.initialConfig.disabled;
16649     },
16650
16651     /**
16652      * Sets the hidden state of all components using this action.  Shortcut method
16653      * for <code>{@link #hide}</code> and <code>{@link #show}</code>.
16654      * @param {Boolean} hidden True to hide the component, false to show it
16655      */
16656     setHidden : function(v){
16657         this.initialConfig.hidden = v;
16658         this.callEach('setVisible', [!v]);
16659     },
16660
16661     /**
16662      * Shows all components using this action.
16663      */
16664     show : function(){
16665         this.setHidden(false);
16666     },
16667
16668     /**
16669      * Hides all components using this action.
16670      */
16671     hide : function(){
16672         this.setHidden(true);
16673     },
16674
16675     /**
16676      * Returns true if the components using this action are currently hidden, else returns false.  
16677      */
16678     isHidden : function(){
16679         return this.initialConfig.hidden;
16680     },
16681
16682     /**
16683      * Sets the function that will be called by each Component using this action when its primary event is triggered.
16684      * @param {Function} fn The function that will be invoked by the action's components.  The function
16685      * will be called with no arguments.
16686      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.
16687      */
16688     setHandler : function(fn, scope){
16689         this.initialConfig.handler = fn;
16690         this.initialConfig.scope = scope;
16691         this.callEach('setHandler', [fn, scope]);
16692     },
16693
16694     /**
16695      * Executes the specified function once for each Component currently tied to this action.  The function passed
16696      * in should accept a single argument that will be an object that supports the basic Action config/method interface.
16697      * @param {Function} fn The function to execute for each component
16698      * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed.  Defaults to the Component.
16699      */
16700     each : function(fn, scope){
16701         Ext.each(this.items, fn, scope);
16702     },
16703
16704     // private
16705     callEach : function(fnName, args){
16706         var cs = this.items;
16707         for(var i = 0, len = cs.length; i < len; i++){
16708             cs[i][fnName].apply(cs[i], args);
16709         }
16710     },
16711
16712     // private
16713     addComponent : function(comp){
16714         this.items.push(comp);
16715         comp.on('destroy', this.removeComponent, this);
16716     },
16717
16718     // private
16719     removeComponent : function(comp){
16720         this.items.remove(comp);
16721     },
16722
16723     /**
16724      * Executes this action manually using the handler function specified in the original config object
16725      * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this
16726      * function will be passed on to the handler function.
16727      * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function
16728      * @param {Mixed} arg2 (optional)
16729      * @param {Mixed} etc... (optional)
16730      */
16731     execute : function(){
16732         this.initialConfig.handler.apply(this.initialConfig.scope || window, arguments);
16733     }
16734 });
16735 /**
16736  * @class Ext.Layer
16737  * @extends Ext.Element
16738  * An extended {@link Ext.Element} object that supports a shadow and shim, constrain to viewport and
16739  * automatic maintaining of shadow/shim positions.
16740  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16741  * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
16742  * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
16743  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
16744  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16745  * @cfg {String} cls CSS class to add to the element
16746  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16747  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
16748  * @cfg {Boolean} useDisplay
16749  * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
16750  * to use css style <tt>'display:none;'</tt> to hide the Layer.
16751  * @constructor
16752  * @param {Object} config An object with config options.
16753  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16754  */
16755 (function(){
16756 Ext.Layer = function(config, existingEl){
16757     config = config || {};
16758     var dh = Ext.DomHelper;
16759     var cp = config.parentEl, pel = cp ? Ext.getDom(cp) : document.body;
16760     if(existingEl){
16761         this.dom = Ext.getDom(existingEl);
16762     }
16763     if(!this.dom){
16764         var o = config.dh || {tag: 'div', cls: 'x-layer'};
16765         this.dom = dh.append(pel, o);
16766     }
16767     if(config.cls){
16768         this.addClass(config.cls);
16769     }
16770     this.constrain = config.constrain !== false;
16771     this.setVisibilityMode(Ext.Element.VISIBILITY);
16772     if(config.id){
16773         this.id = this.dom.id = config.id;
16774     }else{
16775         this.id = Ext.id(this.dom);
16776     }
16777     this.zindex = config.zindex || this.getZIndex();
16778     this.position('absolute', this.zindex);
16779     if(config.shadow){
16780         this.shadowOffset = config.shadowOffset || 4;
16781         this.shadow = new Ext.Shadow({
16782             offset : this.shadowOffset,
16783             mode : config.shadow
16784         });
16785     }else{
16786         this.shadowOffset = 0;
16787     }
16788     this.useShim = config.shim !== false && Ext.useShims;
16789     this.useDisplay = config.useDisplay;
16790     this.hide();
16791 };
16792
16793 var supr = Ext.Element.prototype;
16794
16795 // shims are shared among layer to keep from having 100 iframes
16796 var shims = [];
16797
16798 Ext.extend(Ext.Layer, Ext.Element, {
16799
16800     getZIndex : function(){
16801         return this.zindex || parseInt((this.getShim() || this).getStyle('z-index'), 10) || 11000;
16802     },
16803
16804     getShim : function(){
16805         if(!this.useShim){
16806             return null;
16807         }
16808         if(this.shim){
16809             return this.shim;
16810         }
16811         var shim = shims.shift();
16812         if(!shim){
16813             shim = this.createShim();
16814             shim.enableDisplayMode('block');
16815             shim.dom.style.display = 'none';
16816             shim.dom.style.visibility = 'visible';
16817         }
16818         var pn = this.dom.parentNode;
16819         if(shim.dom.parentNode != pn){
16820             pn.insertBefore(shim.dom, this.dom);
16821         }
16822         shim.setStyle('z-index', this.getZIndex()-2);
16823         this.shim = shim;
16824         return shim;
16825     },
16826
16827     hideShim : function(){
16828         if(this.shim){
16829             this.shim.setDisplayed(false);
16830             shims.push(this.shim);
16831             delete this.shim;
16832         }
16833     },
16834
16835     disableShadow : function(){
16836         if(this.shadow){
16837             this.shadowDisabled = true;
16838             this.shadow.hide();
16839             this.lastShadowOffset = this.shadowOffset;
16840             this.shadowOffset = 0;
16841         }
16842     },
16843
16844     enableShadow : function(show){
16845         if(this.shadow){
16846             this.shadowDisabled = false;
16847             this.shadowOffset = this.lastShadowOffset;
16848             delete this.lastShadowOffset;
16849             if(show){
16850                 this.sync(true);
16851             }
16852         }
16853     },
16854
16855     // private
16856     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16857     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16858     sync : function(doShow){
16859         var shadow = this.shadow;
16860         if(!this.updating && this.isVisible() && (shadow || this.useShim)){
16861             var shim = this.getShim(),
16862                 w = this.getWidth(),
16863                 h = this.getHeight(),
16864                 l = this.getLeft(true),
16865                 t = this.getTop(true);
16866
16867             if(shadow && !this.shadowDisabled){
16868                 if(doShow && !shadow.isVisible()){
16869                     shadow.show(this);
16870                 }else{
16871                     shadow.realign(l, t, w, h);
16872                 }
16873                 if(shim){
16874                     if(doShow){
16875                        shim.show();
16876                     }
16877                     // fit the shim behind the shadow, so it is shimmed too
16878                     var shadowAdj = shadow.el.getXY(), shimStyle = shim.dom.style,
16879                         shadowSize = shadow.el.getSize();
16880                     shimStyle.left = (shadowAdj[0])+'px';
16881                     shimStyle.top = (shadowAdj[1])+'px';
16882                     shimStyle.width = (shadowSize.width)+'px';
16883                     shimStyle.height = (shadowSize.height)+'px';
16884                 }
16885             }else if(shim){
16886                 if(doShow){
16887                    shim.show();
16888                 }
16889                 shim.setSize(w, h);
16890                 shim.setLeftTop(l, t);
16891             }
16892         }
16893     },
16894
16895     // private
16896     destroy : function(){
16897         this.hideShim();
16898         if(this.shadow){
16899             this.shadow.hide();
16900         }
16901         this.removeAllListeners();
16902         Ext.removeNode(this.dom);
16903         delete this.dom;
16904     },
16905
16906     remove : function(){
16907         this.destroy();
16908     },
16909
16910     // private
16911     beginUpdate : function(){
16912         this.updating = true;
16913     },
16914
16915     // private
16916     endUpdate : function(){
16917         this.updating = false;
16918         this.sync(true);
16919     },
16920
16921     // private
16922     hideUnders : function(negOffset){
16923         if(this.shadow){
16924             this.shadow.hide();
16925         }
16926         this.hideShim();
16927     },
16928
16929     // private
16930     constrainXY : function(){
16931         if(this.constrain){
16932             var vw = Ext.lib.Dom.getViewWidth(),
16933                 vh = Ext.lib.Dom.getViewHeight();
16934             var s = Ext.getDoc().getScroll();
16935
16936             var xy = this.getXY();
16937             var x = xy[0], y = xy[1];
16938             var so = this.shadowOffset;
16939             var w = this.dom.offsetWidth+so, h = this.dom.offsetHeight+so;
16940             // only move it if it needs it
16941             var moved = false;
16942             // first validate right/bottom
16943             if((x + w) > vw+s.left){
16944                 x = vw - w - so;
16945                 moved = true;
16946             }
16947             if((y + h) > vh+s.top){
16948                 y = vh - h - so;
16949                 moved = true;
16950             }
16951             // then make sure top/left isn't negative
16952             if(x < s.left){
16953                 x = s.left;
16954                 moved = true;
16955             }
16956             if(y < s.top){
16957                 y = s.top;
16958                 moved = true;
16959             }
16960             if(moved){
16961                 if(this.avoidY){
16962                     var ay = this.avoidY;
16963                     if(y <= ay && (y+h) >= ay){
16964                         y = ay-h-5;
16965                     }
16966                 }
16967                 xy = [x, y];
16968                 this.storeXY(xy);
16969                 supr.setXY.call(this, xy);
16970                 this.sync();
16971             }
16972         }
16973         return this;
16974     },
16975
16976     isVisible : function(){
16977         return this.visible;
16978     },
16979
16980     // private
16981     showAction : function(){
16982         this.visible = true; // track visibility to prevent getStyle calls
16983         if(this.useDisplay === true){
16984             this.setDisplayed('');
16985         }else if(this.lastXY){
16986             supr.setXY.call(this, this.lastXY);
16987         }else if(this.lastLT){
16988             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16989         }
16990     },
16991
16992     // private
16993     hideAction : function(){
16994         this.visible = false;
16995         if(this.useDisplay === true){
16996             this.setDisplayed(false);
16997         }else{
16998             this.setLeftTop(-10000,-10000);
16999         }
17000     },
17001
17002     // overridden Element method
17003     setVisible : function(v, a, d, c, e){
17004         if(v){
17005             this.showAction();
17006         }
17007         if(a && v){
17008             var cb = function(){
17009                 this.sync(true);
17010                 if(c){
17011                     c();
17012                 }
17013             }.createDelegate(this);
17014             supr.setVisible.call(this, true, true, d, cb, e);
17015         }else{
17016             if(!v){
17017                 this.hideUnders(true);
17018             }
17019             var cb = c;
17020             if(a){
17021                 cb = function(){
17022                     this.hideAction();
17023                     if(c){
17024                         c();
17025                     }
17026                 }.createDelegate(this);
17027             }
17028             supr.setVisible.call(this, v, a, d, cb, e);
17029             if(v){
17030                 this.sync(true);
17031             }else if(!a){
17032                 this.hideAction();
17033             }
17034         }
17035         return this;
17036     },
17037
17038     storeXY : function(xy){
17039         delete this.lastLT;
17040         this.lastXY = xy;
17041     },
17042
17043     storeLeftTop : function(left, top){
17044         delete this.lastXY;
17045         this.lastLT = [left, top];
17046     },
17047
17048     // private
17049     beforeFx : function(){
17050         this.beforeAction();
17051         return Ext.Layer.superclass.beforeFx.apply(this, arguments);
17052     },
17053
17054     // private
17055     afterFx : function(){
17056         Ext.Layer.superclass.afterFx.apply(this, arguments);
17057         this.sync(this.isVisible());
17058     },
17059
17060     // private
17061     beforeAction : function(){
17062         if(!this.updating && this.shadow){
17063             this.shadow.hide();
17064         }
17065     },
17066
17067     // overridden Element method
17068     setLeft : function(left){
17069         this.storeLeftTop(left, this.getTop(true));
17070         supr.setLeft.apply(this, arguments);
17071         this.sync();
17072         return this;
17073     },
17074
17075     setTop : function(top){
17076         this.storeLeftTop(this.getLeft(true), top);
17077         supr.setTop.apply(this, arguments);
17078         this.sync();
17079         return this;
17080     },
17081
17082     setLeftTop : function(left, top){
17083         this.storeLeftTop(left, top);
17084         supr.setLeftTop.apply(this, arguments);
17085         this.sync();
17086         return this;
17087     },
17088
17089     setXY : function(xy, a, d, c, e){
17090         this.fixDisplay();
17091         this.beforeAction();
17092         this.storeXY(xy);
17093         var cb = this.createCB(c);
17094         supr.setXY.call(this, xy, a, d, cb, e);
17095         if(!a){
17096             cb();
17097         }
17098         return this;
17099     },
17100
17101     // private
17102     createCB : function(c){
17103         var el = this;
17104         return function(){
17105             el.constrainXY();
17106             el.sync(true);
17107             if(c){
17108                 c();
17109             }
17110         };
17111     },
17112
17113     // overridden Element method
17114     setX : function(x, a, d, c, e){
17115         this.setXY([x, this.getY()], a, d, c, e);
17116         return this;
17117     },
17118
17119     // overridden Element method
17120     setY : function(y, a, d, c, e){
17121         this.setXY([this.getX(), y], a, d, c, e);
17122         return this;
17123     },
17124
17125     // overridden Element method
17126     setSize : function(w, h, a, d, c, e){
17127         this.beforeAction();
17128         var cb = this.createCB(c);
17129         supr.setSize.call(this, w, h, a, d, cb, e);
17130         if(!a){
17131             cb();
17132         }
17133         return this;
17134     },
17135
17136     // overridden Element method
17137     setWidth : function(w, a, d, c, e){
17138         this.beforeAction();
17139         var cb = this.createCB(c);
17140         supr.setWidth.call(this, w, a, d, cb, e);
17141         if(!a){
17142             cb();
17143         }
17144         return this;
17145     },
17146
17147     // overridden Element method
17148     setHeight : function(h, a, d, c, e){
17149         this.beforeAction();
17150         var cb = this.createCB(c);
17151         supr.setHeight.call(this, h, a, d, cb, e);
17152         if(!a){
17153             cb();
17154         }
17155         return this;
17156     },
17157
17158     // overridden Element method
17159     setBounds : function(x, y, w, h, a, d, c, e){
17160         this.beforeAction();
17161         var cb = this.createCB(c);
17162         if(!a){
17163             this.storeXY([x, y]);
17164             supr.setXY.call(this, [x, y]);
17165             supr.setSize.call(this, w, h, a, d, cb, e);
17166             cb();
17167         }else{
17168             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17169         }
17170         return this;
17171     },
17172
17173     /**
17174      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17175      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17176      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17177      * @param {Number} zindex The new z-index to set
17178      * @return {this} The Layer
17179      */
17180     setZIndex : function(zindex){
17181         this.zindex = zindex;
17182         this.setStyle('z-index', zindex + 2);
17183         if(this.shadow){
17184             this.shadow.setZIndex(zindex + 1);
17185         }
17186         if(this.shim){
17187             this.shim.setStyle('z-index', zindex);
17188         }
17189         return this;
17190     }
17191 });
17192 })();
17193 /**
17194  * @class Ext.Shadow
17195  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
17196  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
17197  * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
17198  * @constructor
17199  * Create a new Shadow
17200  * @param {Object} config The config object
17201  */
17202 Ext.Shadow = function(config){
17203     Ext.apply(this, config);
17204     if(typeof this.mode != "string"){
17205         this.mode = this.defaultMode;
17206     }
17207     var o = this.offset, a = {h: 0};
17208     var rad = Math.floor(this.offset/2);
17209     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
17210         case "drop":
17211             a.w = 0;
17212             a.l = a.t = o;
17213             a.t -= 1;
17214             if(Ext.isIE){
17215                 a.l -= this.offset + rad;
17216                 a.t -= this.offset + rad;
17217                 a.w -= rad;
17218                 a.h -= rad;
17219                 a.t += 1;
17220             }
17221         break;
17222         case "sides":
17223             a.w = (o*2);
17224             a.l = -o;
17225             a.t = o-1;
17226             if(Ext.isIE){
17227                 a.l -= (this.offset - rad);
17228                 a.t -= this.offset + rad;
17229                 a.l += 1;
17230                 a.w -= (this.offset - rad)*2;
17231                 a.w -= rad + 1;
17232                 a.h -= 1;
17233             }
17234         break;
17235         case "frame":
17236             a.w = a.h = (o*2);
17237             a.l = a.t = -o;
17238             a.t += 1;
17239             a.h -= 2;
17240             if(Ext.isIE){
17241                 a.l -= (this.offset - rad);
17242                 a.t -= (this.offset - rad);
17243                 a.l += 1;
17244                 a.w -= (this.offset + rad + 1);
17245                 a.h -= (this.offset + rad);
17246                 a.h += 1;
17247             }
17248         break;
17249     };
17250
17251     this.adjusts = a;
17252 };
17253
17254 Ext.Shadow.prototype = {
17255     /**
17256      * @cfg {String} mode
17257      * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
17258      * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
17259      * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
17260      * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
17261      * </ul></div>
17262      */
17263     /**
17264      * @cfg {String} offset
17265      * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
17266      */
17267     offset: 4,
17268
17269     // private
17270     defaultMode: "drop",
17271
17272     /**
17273      * Displays the shadow under the target element
17274      * @param {Mixed} targetEl The id or element under which the shadow should display
17275      */
17276     show : function(target){
17277         target = Ext.get(target);
17278         if(!this.el){
17279             this.el = Ext.Shadow.Pool.pull();
17280             if(this.el.dom.nextSibling != target.dom){
17281                 this.el.insertBefore(target);
17282             }
17283         }
17284         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
17285         if(Ext.isIE){
17286             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
17287         }
17288         this.realign(
17289             target.getLeft(true),
17290             target.getTop(true),
17291             target.getWidth(),
17292             target.getHeight()
17293         );
17294         this.el.dom.style.display = "block";
17295     },
17296
17297     /**
17298      * Returns true if the shadow is visible, else false
17299      */
17300     isVisible : function(){
17301         return this.el ? true : false;  
17302     },
17303
17304     /**
17305      * Direct alignment when values are already available. Show must be called at least once before
17306      * calling this method to ensure it is initialized.
17307      * @param {Number} left The target element left position
17308      * @param {Number} top The target element top position
17309      * @param {Number} width The target element width
17310      * @param {Number} height The target element height
17311      */
17312     realign : function(l, t, w, h){
17313         if(!this.el){
17314             return;
17315         }
17316         var a = this.adjusts, d = this.el.dom, s = d.style;
17317         var iea = 0;
17318         s.left = (l+a.l)+"px";
17319         s.top = (t+a.t)+"px";
17320         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
17321         if(s.width != sws || s.height != shs){
17322             s.width = sws;
17323             s.height = shs;
17324             if(!Ext.isIE){
17325                 var cn = d.childNodes;
17326                 var sww = Math.max(0, (sw-12))+"px";
17327                 cn[0].childNodes[1].style.width = sww;
17328                 cn[1].childNodes[1].style.width = sww;
17329                 cn[2].childNodes[1].style.width = sww;
17330                 cn[1].style.height = Math.max(0, (sh-12))+"px";
17331             }
17332         }
17333     },
17334
17335     /**
17336      * Hides this shadow
17337      */
17338     hide : function(){
17339         if(this.el){
17340             this.el.dom.style.display = "none";
17341             Ext.Shadow.Pool.push(this.el);
17342             delete this.el;
17343         }
17344     },
17345
17346     /**
17347      * Adjust the z-index of this shadow
17348      * @param {Number} zindex The new z-index
17349      */
17350     setZIndex : function(z){
17351         this.zIndex = z;
17352         if(this.el){
17353             this.el.setStyle("z-index", z);
17354         }
17355     }
17356 };
17357
17358 // Private utility class that manages the internal Shadow cache
17359 Ext.Shadow.Pool = function(){
17360     var p = [];
17361     var markup = Ext.isIE ?
17362                  '<div class="x-ie-shadow"></div>' :
17363                  '<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>';
17364     return {
17365         pull : function(){
17366             var sh = p.shift();
17367             if(!sh){
17368                 sh = Ext.get(Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
17369                 sh.autoBoxAdjust = false;
17370             }
17371             return sh;
17372         },
17373
17374         push : function(sh){
17375             p.push(sh);
17376         }
17377     };
17378 }();/**
17379  * @class Ext.BoxComponent
17380  * @extends Ext.Component
17381  * <p>Base class for any {@link Ext.Component Component} that is to be sized as a box, using width and height.</p>
17382  * <p>BoxComponent provides automatic box model adjustments for sizing and positioning and will work correctly
17383  * within the Component rendering model.</p>
17384  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing
17385  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,
17386  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>
17387  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the
17388  * element to reference:<pre><code>
17389 var pageHeader = new Ext.BoxComponent({
17390     el: 'my-header-div'
17391 });</code></pre>
17392  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>
17393  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the
17394  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a
17395  * {@link Ext.DomHelper DomHelper} specification:<pre><code>
17396 var myImage = new Ext.BoxComponent({
17397     autoEl: {
17398         tag: 'img',
17399         src: '/images/my-image.jpg'
17400     }
17401 });</code></pre></p>
17402  * @constructor
17403  * @param {Ext.Element/String/Object} config The configuration options.
17404  * @xtype box
17405  */
17406 Ext.BoxComponent = Ext.extend(Ext.Component, {
17407
17408     // Configs below are used for all Components when rendered by BoxLayout.
17409     /**
17410      * @cfg {Number} flex
17411      * <p><b>Note</b>: this config is only used when this Component is rendered
17412      * by a Container which has been configured to use a <b>{@link Ext.layout.BoxLayout BoxLayout}.</b>
17413      * Each child Component with a <code>flex</code> property will be flexed either vertically (by a VBoxLayout)
17414      * or horizontally (by an HBoxLayout) according to the item's <b>relative</b> <code>flex</code> value
17415      * compared to the sum of all Components with <code>flex</flex> value specified. Any child items that have
17416      * either a <code>flex = 0</code> or <code>flex = undefined</code> will not be 'flexed' (the initial size will not be changed).
17417      */
17418     // Configs below are used for all Components when rendered by AnchorLayout.
17419     /**
17420      * @cfg {String} anchor <p><b>Note</b>: this config is only used when this Component is rendered
17421      * by a Container which has been configured to use an <b>{@link Ext.layout.AnchorLayout AnchorLayout} (or subclass thereof).</b>
17422      * based layout manager, for example:<div class="mdetail-params"><ul>
17423      * <li>{@link Ext.form.FormPanel}</li>
17424      * <li>specifying <code>layout: 'anchor' // or 'form', or 'absolute'</code></li>
17425      * </ul></div></p>
17426      * <p>See {@link Ext.layout.AnchorLayout}.{@link Ext.layout.AnchorLayout#anchor anchor} also.</p>
17427      */
17428     // tabTip config is used when a BoxComponent is a child of a TabPanel
17429     /**
17430      * @cfg {String} tabTip
17431      * <p><b>Note</b>: this config is only used when this BoxComponent is a child item of a TabPanel.</p>
17432      * A string to be used as innerHTML (html tags are accepted) to show in a tooltip when mousing over
17433      * the associated tab selector element. {@link Ext.QuickTips}.init()
17434      * must be called in order for the tips to render.
17435      */
17436     // Configs below are used for all Components when rendered by BorderLayout.
17437     /**
17438      * @cfg {String} region <p><b>Note</b>: this config is only used when this BoxComponent is rendered
17439      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
17440      * layout manager (e.g. specifying <tt>layout:'border'</tt>).</p><br>
17441      * <p>See {@link Ext.layout.BorderLayout} also.</p>
17442      */
17443     // margins config is used when a BoxComponent is rendered by BorderLayout or BoxLayout.
17444     /**
17445      * @cfg {Object} margins <p><b>Note</b>: this config is only used when this BoxComponent is rendered
17446      * by a Container which has been configured to use the <b>{@link Ext.layout.BorderLayout BorderLayout}</b>
17447      * or one of the two <b>{@link Ext.layout.BoxLayout BoxLayout} subclasses.</b></p>
17448      * <p>An object containing margins to apply to this BoxComponent in the
17449      * format:</p><pre><code>
17450 {
17451     top: (top margin),
17452     right: (right margin),
17453     bottom: (bottom margin),
17454     left: (left margin)
17455 }</code></pre>
17456      * <p>May also be a string containing space-separated, numeric margin values. The order of the
17457      * sides associated with each value matches the way CSS processes margin values:</p>
17458      * <p><div class="mdetail-params"><ul>
17459      * <li>If there is only one value, it applies to all sides.</li>
17460      * <li>If there are two values, the top and bottom borders are set to the first value and the
17461      * right and left are set to the second.</li>
17462      * <li>If there are three values, the top is set to the first value, the left and right are set
17463      * to the second, and the bottom is set to the third.</li>
17464      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
17465      * </ul></div></p>
17466      * <p>Defaults to:</p><pre><code>
17467      * {top:0, right:0, bottom:0, left:0}
17468      * </code></pre>
17469      */
17470     /**
17471      * @cfg {Number} x
17472      * The local x (left) coordinate for this component if contained within a positioning container.
17473      */
17474     /**
17475      * @cfg {Number} y
17476      * The local y (top) coordinate for this component if contained within a positioning container.
17477      */
17478     /**
17479      * @cfg {Number} pageX
17480      * The page level x coordinate for this component if contained within a positioning container.
17481      */
17482     /**
17483      * @cfg {Number} pageY
17484      * The page level y coordinate for this component if contained within a positioning container.
17485      */
17486     /**
17487      * @cfg {Number} height
17488      * The height of this component in pixels (defaults to auto).
17489      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
17490      */
17491     /**
17492      * @cfg {Number} width
17493      * The width of this component in pixels (defaults to auto).
17494      * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
17495      */
17496     /**
17497      * @cfg {Number} boxMinHeight
17498      * <p>The minimum value in pixels which this BoxComponent will set its height to.</p>
17499      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17500      */
17501     /**
17502      * @cfg {Number} boxMinWidth
17503      * <p>The minimum value in pixels which this BoxComponent will set its width to.</p>
17504      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17505      */
17506     /**
17507      * @cfg {Number} boxMaxHeight
17508      * <p>The maximum value in pixels which this BoxComponent will set its height to.</p>
17509      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17510      */
17511     /**
17512      * @cfg {Number} boxMaxWidth
17513      * <p>The maximum value in pixels which this BoxComponent will set its width to.</p>
17514      * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
17515      */
17516     /**
17517      * @cfg {Boolean} autoHeight
17518      * <p>True to use height:'auto', false to use fixed height (or allow it to be managed by its parent
17519      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
17520      * <p><b>Note</b>: Although many components inherit this config option, not all will
17521      * function as expected with a height of 'auto'. Setting autoHeight:true means that the
17522      * browser will manage height based on the element's contents, and that Ext will not manage it at all.</p>
17523      * <p>If the <i>browser</i> is managing the height, be aware that resizes performed by the browser in response
17524      * to changes within the structure of the Component cannot be detected. Therefore changes to the height might
17525      * result in elements needing to be synchronized with the new height. Example:</p><pre><code>
17526 var w = new Ext.Window({
17527     title: 'Window',
17528     width: 600,
17529     autoHeight: true,
17530     items: {
17531         title: 'Collapse Me',
17532         height: 400,
17533         collapsible: true,
17534         border: false,
17535         listeners: {
17536             beforecollapse: function() {
17537                 w.el.shadow.hide();
17538             },
17539             beforeexpand: function() {
17540                 w.el.shadow.hide();
17541             },
17542             collapse: function() {
17543                 w.syncShadow();
17544             },
17545             expand: function() {
17546                 w.syncShadow();
17547             }
17548         }
17549     }
17550 }).show();
17551 </code></pre>
17552      */
17553     /**
17554      * @cfg {Boolean} autoWidth
17555      * <p>True to use width:'auto', false to use fixed width (or allow it to be managed by its parent
17556      * Container's {@link Ext.Container#layout layout manager}. Defaults to false.</p>
17557      * <p><b>Note</b>: Although many components  inherit this config option, not all will
17558      * function as expected with a width of 'auto'. Setting autoWidth:true means that the
17559      * browser will manage width based on the element's contents, and that Ext will not manage it at all.</p>
17560      * <p>If the <i>browser</i> is managing the width, be aware that resizes performed by the browser in response
17561      * to changes within the structure of the Component cannot be detected. Therefore changes to the width might
17562      * result in elements needing to be synchronized with the new width. For example, where the target element is:</p><pre><code>
17563 &lt;div id='grid-container' style='margin-left:25%;width:50%'>&lt;/div>
17564 </code></pre>
17565      * A Panel rendered into that target element must listen for browser window resize in order to relay its
17566       * child items when the browser changes its width:<pre><code>
17567 var myPanel = new Ext.Panel({
17568     renderTo: 'grid-container',
17569     monitorResize: true, // relay on browser resize
17570     title: 'Panel',
17571     height: 400,
17572     autoWidth: true,
17573     layout: 'hbox',
17574     layoutConfig: {
17575         align: 'stretch'
17576     },
17577     defaults: {
17578         flex: 1
17579     },
17580     items: [{
17581         title: 'Box 1',
17582     }, {
17583         title: 'Box 2'
17584     }, {
17585         title: 'Box 3'
17586     }],
17587 });
17588 </code></pre>
17589      */
17590     /**
17591      * @cfg {Boolean} autoScroll
17592      * <code>true</code> to use overflow:'auto' on the components layout element and show scroll bars automatically when
17593      * necessary, <code>false</code> to clip any overflowing content (defaults to <code>false</code>).
17594      */
17595
17596     /* // private internal config
17597      * {Boolean} deferHeight
17598      * True to defer height calculations to an external component, false to allow this component to set its own
17599      * height (defaults to false).
17600      */
17601
17602     // private
17603     initComponent : function(){
17604         Ext.BoxComponent.superclass.initComponent.call(this);
17605         this.addEvents(
17606             /**
17607              * @event resize
17608              * Fires after the component is resized.
17609              * @param {Ext.Component} this
17610              * @param {Number} adjWidth The box-adjusted width that was set
17611              * @param {Number} adjHeight The box-adjusted height that was set
17612              * @param {Number} rawWidth The width that was originally specified
17613              * @param {Number} rawHeight The height that was originally specified
17614              */
17615             'resize',
17616             /**
17617              * @event move
17618              * Fires after the component is moved.
17619              * @param {Ext.Component} this
17620              * @param {Number} x The new x position
17621              * @param {Number} y The new y position
17622              */
17623             'move'
17624         );
17625     },
17626
17627     // private, set in afterRender to signify that the component has been rendered
17628     boxReady : false,
17629     // private, used to defer height settings to subclasses
17630     deferHeight: false,
17631
17632     /**
17633      * Sets the width and height of this BoxComponent. This method fires the {@link #resize} event. This method can accept
17634      * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
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}s (by default, pixels).</li>
17637      * <li>A String used to set the CSS width style.</li>
17638      * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
17639      * <li><code>undefined</code> to leave the width unchanged.</li>
17640      * </ul></div>
17641      * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
17642      * This may be one of:<div class="mdetail-params"><ul>
17643      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit}s (by default, pixels).</li>
17644      * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
17645      * <li><code>undefined</code> to leave the height unchanged.</li>
17646      * </ul></div>
17647      * @return {Ext.BoxComponent} this
17648      */
17649     setSize : function(w, h){
17650
17651         // support for standard size objects
17652         if(typeof w == 'object'){
17653             h = w.height;
17654             w = w.width;
17655         }
17656         if (Ext.isDefined(w) && Ext.isDefined(this.boxMinWidth) && (w < this.boxMinWidth)) {
17657             w = this.boxMinWidth;
17658         }
17659         if (Ext.isDefined(h) && Ext.isDefined(this.boxMinHeight) && (h < this.boxMinHeight)) {
17660             h = this.boxMinHeight;
17661         }
17662         if (Ext.isDefined(w) && Ext.isDefined(this.boxMaxWidth) && (w > this.boxMaxWidth)) {
17663             w = this.boxMaxWidth;
17664         }
17665         if (Ext.isDefined(h) && Ext.isDefined(this.boxMaxHeight) && (h > this.boxMaxHeight)) {
17666             h = this.boxMaxHeight;
17667         }
17668         // not rendered
17669         if(!this.boxReady){
17670             this.width  = w;
17671             this.height = h;
17672             return this;
17673         }
17674
17675         // prevent recalcs when not needed
17676         if(this.cacheSizes !== false && this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17677             return this;
17678         }
17679         this.lastSize = {width: w, height: h};
17680         var adj = this.adjustSize(w, h),
17681             aw = adj.width,
17682             ah = adj.height,
17683             rz;
17684         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17685             rz = this.getResizeEl();
17686             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17687                 rz.setSize(aw, ah);
17688             }else if(!this.deferHeight && ah !== undefined){
17689                 rz.setHeight(ah);
17690             }else if(aw !== undefined){
17691                 rz.setWidth(aw);
17692             }
17693             this.onResize(aw, ah, w, h);
17694             this.fireEvent('resize', this, aw, ah, w, h);
17695         }
17696         return this;
17697     },
17698
17699     /**
17700      * Sets the width of the component.  This method fires the {@link #resize} event.
17701      * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
17702      * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
17703      * <li>A String used to set the CSS width style.</li>
17704      * </ul></div>
17705      * @return {Ext.BoxComponent} this
17706      */
17707     setWidth : function(width){
17708         return this.setSize(width);
17709     },
17710
17711     /**
17712      * Sets the height of the component.  This method fires the {@link #resize} event.
17713      * @param {Mixed} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
17714      * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.Element#defaultUnit defaultUnit}s (by default, pixels).</li>
17715      * <li>A String used to set the CSS height style.</li>
17716      * <li><i>undefined</i> to leave the height unchanged.</li>
17717      * </ul></div>
17718      * @return {Ext.BoxComponent} this
17719      */
17720     setHeight : function(height){
17721         return this.setSize(undefined, height);
17722     },
17723
17724     /**
17725      * Gets the current size of the component's underlying element.
17726      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17727      */
17728     getSize : function(){
17729         return this.getResizeEl().getSize();
17730     },
17731
17732     /**
17733      * Gets the current width of the component's underlying element.
17734      * @return {Number}
17735      */
17736     getWidth : function(){
17737         return this.getResizeEl().getWidth();
17738     },
17739
17740     /**
17741      * Gets the current height of the component's underlying element.
17742      * @return {Number}
17743      */
17744     getHeight : function(){
17745         return this.getResizeEl().getHeight();
17746     },
17747
17748     /**
17749      * Gets the current size of the component's underlying element, including space taken by its margins.
17750      * @return {Object} An object containing the element's size {width: (element width + left/right margins), height: (element height + top/bottom margins)}
17751      */
17752     getOuterSize : function(){
17753         var el = this.getResizeEl();
17754         return {width: el.getWidth() + el.getMargins('lr'),
17755                 height: el.getHeight() + el.getMargins('tb')};
17756     },
17757
17758     /**
17759      * Gets the current XY position of the component's underlying element.
17760      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17761      * @return {Array} The XY position of the element (e.g., [100, 200])
17762      */
17763     getPosition : function(local){
17764         var el = this.getPositionEl();
17765         if(local === true){
17766             return [el.getLeft(true), el.getTop(true)];
17767         }
17768         return this.xy || el.getXY();
17769     },
17770
17771     /**
17772      * Gets the current box measurements of the component's underlying element.
17773      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17774      * @return {Object} box An object in the format {x, y, width, height}
17775      */
17776     getBox : function(local){
17777         var pos = this.getPosition(local);
17778         var s = this.getSize();
17779         s.x = pos[0];
17780         s.y = pos[1];
17781         return s;
17782     },
17783
17784     /**
17785      * Sets the current box measurements of the component's underlying element.
17786      * @param {Object} box An object in the format {x, y, width, height}
17787      * @return {Ext.BoxComponent} this
17788      */
17789     updateBox : function(box){
17790         this.setSize(box.width, box.height);
17791         this.setPagePosition(box.x, box.y);
17792         return this;
17793     },
17794
17795     /**
17796      * <p>Returns the outermost Element of this Component which defines the Components overall size.</p>
17797      * <p><i>Usually</i> this will return the same Element as <code>{@link #getEl}</code>,
17798      * but in some cases, a Component may have some more wrapping Elements around its main
17799      * active Element.</p>
17800      * <p>An example is a ComboBox. It is encased in a <i>wrapping</i> Element which
17801      * contains both the <code>&lt;input></code> Element (which is what would be returned
17802      * by its <code>{@link #getEl}</code> method, <i>and</i> the trigger button Element.
17803      * This Element is returned as the <code>resizeEl</code>.
17804      * @return {Ext.Element} The Element which is to be resized by size managing layouts.
17805      */
17806     getResizeEl : function(){
17807         return this.resizeEl || this.el;
17808     },
17809
17810     /**
17811      * Sets the overflow on the content element of the component.
17812      * @param {Boolean} scroll True to allow the Component to auto scroll.
17813      * @return {Ext.BoxComponent} this
17814      */
17815     setAutoScroll : function(scroll){
17816         if(this.rendered){
17817             this.getContentTarget().setOverflow(scroll ? 'auto' : '');
17818         }
17819         this.autoScroll = scroll;
17820         return this;
17821     },
17822
17823     /**
17824      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17825      * This method fires the {@link #move} event.
17826      * @param {Number} left The new left
17827      * @param {Number} top The new top
17828      * @return {Ext.BoxComponent} this
17829      */
17830     setPosition : function(x, y){
17831         if(x && typeof x[1] == 'number'){
17832             y = x[1];
17833             x = x[0];
17834         }
17835         this.x = x;
17836         this.y = y;
17837         if(!this.boxReady){
17838             return this;
17839         }
17840         var adj = this.adjustPosition(x, y);
17841         var ax = adj.x, ay = adj.y;
17842
17843         var el = this.getPositionEl();
17844         if(ax !== undefined || ay !== undefined){
17845             if(ax !== undefined && ay !== undefined){
17846                 el.setLeftTop(ax, ay);
17847             }else if(ax !== undefined){
17848                 el.setLeft(ax);
17849             }else if(ay !== undefined){
17850                 el.setTop(ay);
17851             }
17852             this.onPosition(ax, ay);
17853             this.fireEvent('move', this, ax, ay);
17854         }
17855         return this;
17856     },
17857
17858     /**
17859      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17860      * This method fires the {@link #move} event.
17861      * @param {Number} x The new x position
17862      * @param {Number} y The new y position
17863      * @return {Ext.BoxComponent} this
17864      */
17865     setPagePosition : function(x, y){
17866         if(x && typeof x[1] == 'number'){
17867             y = x[1];
17868             x = x[0];
17869         }
17870         this.pageX = x;
17871         this.pageY = y;
17872         if(!this.boxReady){
17873             return;
17874         }
17875         if(x === undefined || y === undefined){ // cannot translate undefined points
17876             return;
17877         }
17878         var p = this.getPositionEl().translatePoints(x, y);
17879         this.setPosition(p.left, p.top);
17880         return this;
17881     },
17882
17883     // private
17884     afterRender : function(){
17885         Ext.BoxComponent.superclass.afterRender.call(this);
17886         if(this.resizeEl){
17887             this.resizeEl = Ext.get(this.resizeEl);
17888         }
17889         if(this.positionEl){
17890             this.positionEl = Ext.get(this.positionEl);
17891         }
17892         this.boxReady = true;
17893         Ext.isDefined(this.autoScroll) && this.setAutoScroll(this.autoScroll);
17894         this.setSize(this.width, this.height);
17895         if(this.x || this.y){
17896             this.setPosition(this.x, this.y);
17897         }else if(this.pageX || this.pageY){
17898             this.setPagePosition(this.pageX, this.pageY);
17899         }
17900     },
17901
17902     /**
17903      * Force the component's size to recalculate based on the underlying element's current height and width.
17904      * @return {Ext.BoxComponent} this
17905      */
17906     syncSize : function(){
17907         delete this.lastSize;
17908         this.setSize(this.autoWidth ? undefined : this.getResizeEl().getWidth(), this.autoHeight ? undefined : this.getResizeEl().getHeight());
17909         return this;
17910     },
17911
17912     /* // protected
17913      * Called after the component is resized, this method is empty by default but can be implemented by any
17914      * subclass that needs to perform custom logic after a resize occurs.
17915      * @param {Number} adjWidth The box-adjusted width that was set
17916      * @param {Number} adjHeight The box-adjusted height that was set
17917      * @param {Number} rawWidth The width that was originally specified
17918      * @param {Number} rawHeight The height that was originally specified
17919      */
17920     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17921     },
17922
17923     /* // protected
17924      * Called after the component is moved, this method is empty by default but can be implemented by any
17925      * subclass that needs to perform custom logic after a move occurs.
17926      * @param {Number} x The new x position
17927      * @param {Number} y The new y position
17928      */
17929     onPosition : function(x, y){
17930
17931     },
17932
17933     // private
17934     adjustSize : function(w, h){
17935         if(this.autoWidth){
17936             w = 'auto';
17937         }
17938         if(this.autoHeight){
17939             h = 'auto';
17940         }
17941         return {width : w, height: h};
17942     },
17943
17944     // private
17945     adjustPosition : function(x, y){
17946         return {x : x, y: y};
17947     }
17948 });
17949 Ext.reg('box', Ext.BoxComponent);
17950
17951
17952 /**
17953  * @class Ext.Spacer
17954  * @extends Ext.BoxComponent
17955  * <p>Used to provide a sizable space in a layout.</p>
17956  * @constructor
17957  * @param {Object} config
17958  */
17959 Ext.Spacer = Ext.extend(Ext.BoxComponent, {
17960     autoEl:'div'
17961 });
17962 Ext.reg('spacer', Ext.Spacer);/**
17963  * @class Ext.SplitBar
17964  * @extends Ext.util.Observable
17965  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
17966  * <br><br>
17967  * Usage:
17968  * <pre><code>
17969 var split = new Ext.SplitBar("elementToDrag", "elementToSize",
17970                    Ext.SplitBar.HORIZONTAL, Ext.SplitBar.LEFT);
17971 split.setAdapter(new Ext.SplitBar.AbsoluteLayoutAdapter("container"));
17972 split.minSize = 100;
17973 split.maxSize = 600;
17974 split.animate = true;
17975 split.on('moved', splitterMoved);
17976 </code></pre>
17977  * @constructor
17978  * Create a new SplitBar
17979  * @param {Mixed} dragElement The element to be dragged and act as the SplitBar.
17980  * @param {Mixed} resizingElement The element to be resized based on where the SplitBar element is dragged
17981  * @param {Number} orientation (optional) Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
17982  * @param {Number} placement (optional) Either Ext.SplitBar.LEFT or Ext.SplitBar.RIGHT for horizontal or
17983                         Ext.SplitBar.TOP or Ext.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
17984                         position of the SplitBar).
17985  */
17986 Ext.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
17987
17988     /** @private */
17989     this.el = Ext.get(dragElement, true);
17990     this.el.dom.unselectable = "on";
17991     /** @private */
17992     this.resizingEl = Ext.get(resizingElement, true);
17993
17994     /**
17995      * @private
17996      * The orientation of the split. Either Ext.SplitBar.HORIZONTAL or Ext.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
17997      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
17998      * @type Number
17999      */
18000     this.orientation = orientation || Ext.SplitBar.HORIZONTAL;
18001
18002     /**
18003      * The increment, in pixels by which to move this SplitBar. When <i>undefined</i>, the SplitBar moves smoothly.
18004      * @type Number
18005      * @property tickSize
18006      */
18007     /**
18008      * The minimum size of the resizing element. (Defaults to 0)
18009      * @type Number
18010      */
18011     this.minSize = 0;
18012
18013     /**
18014      * The maximum size of the resizing element. (Defaults to 2000)
18015      * @type Number
18016      */
18017     this.maxSize = 2000;
18018
18019     /**
18020      * Whether to animate the transition to the new size
18021      * @type Boolean
18022      */
18023     this.animate = false;
18024
18025     /**
18026      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
18027      * @type Boolean
18028      */
18029     this.useShim = false;
18030
18031     /** @private */
18032     this.shim = null;
18033
18034     if(!existingProxy){
18035         /** @private */
18036         this.proxy = Ext.SplitBar.createProxy(this.orientation);
18037     }else{
18038         this.proxy = Ext.get(existingProxy).dom;
18039     }
18040     /** @private */
18041     this.dd = new Ext.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
18042
18043     /** @private */
18044     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
18045
18046     /** @private */
18047     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
18048
18049     /** @private */
18050     this.dragSpecs = {};
18051
18052     /**
18053      * @private The adapter to use to positon and resize elements
18054      */
18055     this.adapter = new Ext.SplitBar.BasicLayoutAdapter();
18056     this.adapter.init(this);
18057
18058     if(this.orientation == Ext.SplitBar.HORIZONTAL){
18059         /** @private */
18060         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Ext.SplitBar.LEFT : Ext.SplitBar.RIGHT);
18061         this.el.addClass("x-splitbar-h");
18062     }else{
18063         /** @private */
18064         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Ext.SplitBar.TOP : Ext.SplitBar.BOTTOM);
18065         this.el.addClass("x-splitbar-v");
18066     }
18067
18068     this.addEvents(
18069         /**
18070          * @event resize
18071          * Fires when the splitter is moved (alias for {@link #moved})
18072          * @param {Ext.SplitBar} this
18073          * @param {Number} newSize the new width or height
18074          */
18075         "resize",
18076         /**
18077          * @event moved
18078          * Fires when the splitter is moved
18079          * @param {Ext.SplitBar} this
18080          * @param {Number} newSize the new width or height
18081          */
18082         "moved",
18083         /**
18084          * @event beforeresize
18085          * Fires before the splitter is dragged
18086          * @param {Ext.SplitBar} this
18087          */
18088         "beforeresize",
18089
18090         "beforeapply"
18091     );
18092
18093     Ext.SplitBar.superclass.constructor.call(this);
18094 };
18095
18096 Ext.extend(Ext.SplitBar, Ext.util.Observable, {
18097     onStartProxyDrag : function(x, y){
18098         this.fireEvent("beforeresize", this);
18099         this.overlay =  Ext.DomHelper.append(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
18100         this.overlay.unselectable();
18101         this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
18102         this.overlay.show();
18103         Ext.get(this.proxy).setDisplayed("block");
18104         var size = this.adapter.getElementSize(this);
18105         this.activeMinSize = this.getMinimumSize();
18106         this.activeMaxSize = this.getMaximumSize();
18107         var c1 = size - this.activeMinSize;
18108         var c2 = Math.max(this.activeMaxSize - size, 0);
18109         if(this.orientation == Ext.SplitBar.HORIZONTAL){
18110             this.dd.resetConstraints();
18111             this.dd.setXConstraint(
18112                 this.placement == Ext.SplitBar.LEFT ? c1 : c2,
18113                 this.placement == Ext.SplitBar.LEFT ? c2 : c1,
18114                 this.tickSize
18115             );
18116             this.dd.setYConstraint(0, 0);
18117         }else{
18118             this.dd.resetConstraints();
18119             this.dd.setXConstraint(0, 0);
18120             this.dd.setYConstraint(
18121                 this.placement == Ext.SplitBar.TOP ? c1 : c2,
18122                 this.placement == Ext.SplitBar.TOP ? c2 : c1,
18123                 this.tickSize
18124             );
18125          }
18126         this.dragSpecs.startSize = size;
18127         this.dragSpecs.startPoint = [x, y];
18128         Ext.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
18129     },
18130
18131     /**
18132      * @private Called after the drag operation by the DDProxy
18133      */
18134     onEndProxyDrag : function(e){
18135         Ext.get(this.proxy).setDisplayed(false);
18136         var endPoint = Ext.lib.Event.getXY(e);
18137         if(this.overlay){
18138             Ext.destroy(this.overlay);
18139             delete this.overlay;
18140         }
18141         var newSize;
18142         if(this.orientation == Ext.SplitBar.HORIZONTAL){
18143             newSize = this.dragSpecs.startSize +
18144                 (this.placement == Ext.SplitBar.LEFT ?
18145                     endPoint[0] - this.dragSpecs.startPoint[0] :
18146                     this.dragSpecs.startPoint[0] - endPoint[0]
18147                 );
18148         }else{
18149             newSize = this.dragSpecs.startSize +
18150                 (this.placement == Ext.SplitBar.TOP ?
18151                     endPoint[1] - this.dragSpecs.startPoint[1] :
18152                     this.dragSpecs.startPoint[1] - endPoint[1]
18153                 );
18154         }
18155         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
18156         if(newSize != this.dragSpecs.startSize){
18157             if(this.fireEvent('beforeapply', this, newSize) !== false){
18158                 this.adapter.setElementSize(this, newSize);
18159                 this.fireEvent("moved", this, newSize);
18160                 this.fireEvent("resize", this, newSize);
18161             }
18162         }
18163     },
18164
18165     /**
18166      * Get the adapter this SplitBar uses
18167      * @return The adapter object
18168      */
18169     getAdapter : function(){
18170         return this.adapter;
18171     },
18172
18173     /**
18174      * Set the adapter this SplitBar uses
18175      * @param {Object} adapter A SplitBar adapter object
18176      */
18177     setAdapter : function(adapter){
18178         this.adapter = adapter;
18179         this.adapter.init(this);
18180     },
18181
18182     /**
18183      * Gets the minimum size for the resizing element
18184      * @return {Number} The minimum size
18185      */
18186     getMinimumSize : function(){
18187         return this.minSize;
18188     },
18189
18190     /**
18191      * Sets the minimum size for the resizing element
18192      * @param {Number} minSize The minimum size
18193      */
18194     setMinimumSize : function(minSize){
18195         this.minSize = minSize;
18196     },
18197
18198     /**
18199      * Gets the maximum size for the resizing element
18200      * @return {Number} The maximum size
18201      */
18202     getMaximumSize : function(){
18203         return this.maxSize;
18204     },
18205
18206     /**
18207      * Sets the maximum size for the resizing element
18208      * @param {Number} maxSize The maximum size
18209      */
18210     setMaximumSize : function(maxSize){
18211         this.maxSize = maxSize;
18212     },
18213
18214     /**
18215      * Sets the initialize size for the resizing element
18216      * @param {Number} size The initial size
18217      */
18218     setCurrentSize : function(size){
18219         var oldAnimate = this.animate;
18220         this.animate = false;
18221         this.adapter.setElementSize(this, size);
18222         this.animate = oldAnimate;
18223     },
18224
18225     /**
18226      * Destroy this splitbar.
18227      * @param {Boolean} removeEl True to remove the element
18228      */
18229     destroy : function(removeEl){
18230         Ext.destroy(this.shim, Ext.get(this.proxy));
18231         this.dd.unreg();
18232         if(removeEl){
18233             this.el.remove();
18234         }
18235         this.purgeListeners();
18236     }
18237 });
18238
18239 /**
18240  * @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.
18241  */
18242 Ext.SplitBar.createProxy = function(dir){
18243     var proxy = new Ext.Element(document.createElement("div"));
18244     document.body.appendChild(proxy.dom);
18245     proxy.unselectable();
18246     var cls = 'x-splitbar-proxy';
18247     proxy.addClass(cls + ' ' + (dir == Ext.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
18248     return proxy.dom;
18249 };
18250
18251 /**
18252  * @class Ext.SplitBar.BasicLayoutAdapter
18253  * Default Adapter. It assumes the splitter and resizing element are not positioned
18254  * elements and only gets/sets the width of the element. Generally used for table based layouts.
18255  */
18256 Ext.SplitBar.BasicLayoutAdapter = function(){
18257 };
18258
18259 Ext.SplitBar.BasicLayoutAdapter.prototype = {
18260     // do nothing for now
18261     init : function(s){
18262
18263     },
18264     /**
18265      * Called before drag operations to get the current size of the resizing element.
18266      * @param {Ext.SplitBar} s The SplitBar using this adapter
18267      */
18268      getElementSize : function(s){
18269         if(s.orientation == Ext.SplitBar.HORIZONTAL){
18270             return s.resizingEl.getWidth();
18271         }else{
18272             return s.resizingEl.getHeight();
18273         }
18274     },
18275
18276     /**
18277      * Called after drag operations to set the size of the resizing element.
18278      * @param {Ext.SplitBar} s The SplitBar using this adapter
18279      * @param {Number} newSize The new size to set
18280      * @param {Function} onComplete A function to be invoked when resizing is complete
18281      */
18282     setElementSize : function(s, newSize, onComplete){
18283         if(s.orientation == Ext.SplitBar.HORIZONTAL){
18284             if(!s.animate){
18285                 s.resizingEl.setWidth(newSize);
18286                 if(onComplete){
18287                     onComplete(s, newSize);
18288                 }
18289             }else{
18290                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
18291             }
18292         }else{
18293
18294             if(!s.animate){
18295                 s.resizingEl.setHeight(newSize);
18296                 if(onComplete){
18297                     onComplete(s, newSize);
18298                 }
18299             }else{
18300                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
18301             }
18302         }
18303     }
18304 };
18305
18306 /**
18307  *@class Ext.SplitBar.AbsoluteLayoutAdapter
18308  * @extends Ext.SplitBar.BasicLayoutAdapter
18309  * Adapter that  moves the splitter element to align with the resized sizing element.
18310  * Used with an absolute positioned SplitBar.
18311  * @param {Mixed} container The container that wraps around the absolute positioned content. If it's
18312  * document.body, make sure you assign an id to the body element.
18313  */
18314 Ext.SplitBar.AbsoluteLayoutAdapter = function(container){
18315     this.basic = new Ext.SplitBar.BasicLayoutAdapter();
18316     this.container = Ext.get(container);
18317 };
18318
18319 Ext.SplitBar.AbsoluteLayoutAdapter.prototype = {
18320     init : function(s){
18321         this.basic.init(s);
18322     },
18323
18324     getElementSize : function(s){
18325         return this.basic.getElementSize(s);
18326     },
18327
18328     setElementSize : function(s, newSize, onComplete){
18329         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
18330     },
18331
18332     moveSplitter : function(s){
18333         var yes = Ext.SplitBar;
18334         switch(s.placement){
18335             case yes.LEFT:
18336                 s.el.setX(s.resizingEl.getRight());
18337                 break;
18338             case yes.RIGHT:
18339                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
18340                 break;
18341             case yes.TOP:
18342                 s.el.setY(s.resizingEl.getBottom());
18343                 break;
18344             case yes.BOTTOM:
18345                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
18346                 break;
18347         }
18348     }
18349 };
18350
18351 /**
18352  * Orientation constant - Create a vertical SplitBar
18353  * @static
18354  * @type Number
18355  */
18356 Ext.SplitBar.VERTICAL = 1;
18357
18358 /**
18359  * Orientation constant - Create a horizontal SplitBar
18360  * @static
18361  * @type Number
18362  */
18363 Ext.SplitBar.HORIZONTAL = 2;
18364
18365 /**
18366  * Placement constant - The resizing element is to the left of the splitter element
18367  * @static
18368  * @type Number
18369  */
18370 Ext.SplitBar.LEFT = 1;
18371
18372 /**
18373  * Placement constant - The resizing element is to the right of the splitter element
18374  * @static
18375  * @type Number
18376  */
18377 Ext.SplitBar.RIGHT = 2;
18378
18379 /**
18380  * Placement constant - The resizing element is positioned above the splitter element
18381  * @static
18382  * @type Number
18383  */
18384 Ext.SplitBar.TOP = 3;
18385
18386 /**
18387  * Placement constant - The resizing element is positioned under splitter element
18388  * @static
18389  * @type Number
18390  */
18391 Ext.SplitBar.BOTTOM = 4;
18392 /**
18393  * @class Ext.Container
18394  * @extends Ext.BoxComponent
18395  * <p>Base class for any {@link Ext.BoxComponent} that may contain other Components. Containers handle the
18396  * basic behavior of containing items, namely adding, inserting and removing items.</p>
18397  *
18398  * <p>The most commonly used Container classes are {@link Ext.Panel}, {@link Ext.Window} and {@link Ext.TabPanel}.
18399  * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
18400  * Container to be encapsulated by an HTML element to your specifications by using the
18401  * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option. This is a useful technique when creating
18402  * embedded {@link Ext.layout.ColumnLayout column} layouts inside {@link Ext.form.FormPanel FormPanels}
18403  * for example.</p>
18404  *
18405  * <p>The code below illustrates both how to explicitly create a Container, and how to implicitly
18406  * create one using the <b><code>'container'</code></b> xtype:<pre><code>
18407 // explicitly create a Container
18408 var embeddedColumns = new Ext.Container({
18409     autoEl: 'div',  // This is the default
18410     layout: 'column',
18411     defaults: {
18412         // implicitly create Container by specifying xtype
18413         xtype: 'container',
18414         autoEl: 'div', // This is the default.
18415         layout: 'form',
18416         columnWidth: 0.5,
18417         style: {
18418             padding: '10px'
18419         }
18420     },
18421 //  The two items below will be Ext.Containers, each encapsulated by a &lt;DIV> element.
18422     items: [{
18423         items: {
18424             xtype: 'datefield',
18425             name: 'startDate',
18426             fieldLabel: 'Start date'
18427         }
18428     }, {
18429         items: {
18430             xtype: 'datefield',
18431             name: 'endDate',
18432             fieldLabel: 'End date'
18433         }
18434     }]
18435 });</code></pre></p>
18436  *
18437  * <p><u><b>Layout</b></u></p>
18438  * <p>Container classes delegate the rendering of child Components to a layout
18439  * manager class which must be configured into the Container using the
18440  * <code><b>{@link #layout}</b></code> configuration property.</p>
18441  * <p>When either specifying child <code>{@link #items}</code> of a Container,
18442  * or dynamically {@link #add adding} Components to a Container, remember to
18443  * consider how you wish the Container to arrange those child elements, and
18444  * whether those child elements need to be sized using one of Ext's built-in
18445  * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
18446  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme which only
18447  * renders child components, appending them one after the other inside the
18448  * Container, and <b>does not apply any sizing</b> at all.</p>
18449  * <p>A common mistake is when a developer neglects to specify a
18450  * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
18451  * TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
18452  * has been specified). If a Container is left to use the default
18453  * {@link Ext.layout.ContainerLayout ContainerLayout} scheme, none of its
18454  * child components will be resized, or changed in any way when the Container
18455  * is resized.</p>
18456  * <p>Certain layout managers allow dynamic addition of child components.
18457  * Those that do include {@link Ext.layout.CardLayout},
18458  * {@link Ext.layout.AnchorLayout}, {@link Ext.layout.FormLayout}, and
18459  * {@link Ext.layout.TableLayout}. For example:<pre><code>
18460 //  Create the GridPanel.
18461 var myNewGrid = new Ext.grid.GridPanel({
18462     store: myStore,
18463     columns: myColumnModel,
18464     title: 'Results', // the title becomes the title of the tab
18465 });
18466
18467 myTabPanel.add(myNewGrid); // {@link Ext.TabPanel} implicitly uses {@link Ext.layout.CardLayout CardLayout}
18468 myTabPanel.{@link Ext.TabPanel#setActiveTab setActiveTab}(myNewGrid);
18469  * </code></pre></p>
18470  * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
18471  * a TabPanel uses {@link Ext.layout.CardLayout} as its layout manager which
18472  * means all its child items are sized to {@link Ext.layout.FitLayout fit}
18473  * exactly into its client area.
18474  * <p><b><u>Overnesting is a common problem</u></b>.
18475  * An example of overnesting occurs when a GridPanel is added to a TabPanel
18476  * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
18477  * <code><b>{@link #layout}</b></code> specified) and then add that wrapping Panel
18478  * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
18479  * Component which can be added directly to a Container. If the wrapping Panel
18480  * has no <code><b>{@link #layout}</b></code> configuration, then the overnested
18481  * GridPanel will not be sized as expected.<p>
18482  *
18483  * <p><u><b>Adding via remote configuration</b></u></p>
18484  *
18485  * <p>A server side script can be used to add Components which are generated dynamically on the server.
18486  * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
18487  * based on certain parameters:
18488  * </p><pre><code>
18489 // execute an Ajax request to invoke server side script:
18490 Ext.Ajax.request({
18491     url: 'gen-invoice-grid.php',
18492     // send additional parameters to instruct server script
18493     params: {
18494         startDate: Ext.getCmp('start-date').getValue(),
18495         endDate: Ext.getCmp('end-date').getValue()
18496     },
18497     // process the response object to add it to the TabPanel:
18498     success: function(xhr) {
18499         var newComponent = eval(xhr.responseText); // see discussion below
18500         myTabPanel.add(newComponent); // add the component to the TabPanel
18501         myTabPanel.setActiveTab(newComponent);
18502     },
18503     failure: function() {
18504         Ext.Msg.alert("Grid create failed", "Server communication failure");
18505     }
18506 });
18507 </code></pre>
18508  * <p>The server script needs to return an executable Javascript statement which, when processed
18509  * using <code>eval()</code>, will return either a config object with an {@link Ext.Component#xtype xtype},
18510  * or an instantiated Component. The server might return this for example:</p><pre><code>
18511 (function() {
18512     function formatDate(value){
18513         return value ? value.dateFormat('M d, Y') : '';
18514     };
18515
18516     var store = new Ext.data.Store({
18517         url: 'get-invoice-data.php',
18518         baseParams: {
18519             startDate: '01/01/2008',
18520             endDate: '01/31/2008'
18521         },
18522         reader: new Ext.data.JsonReader({
18523             record: 'transaction',
18524             idProperty: 'id',
18525             totalRecords: 'total'
18526         }, [
18527            'customer',
18528            'invNo',
18529            {name: 'date', type: 'date', dateFormat: 'm/d/Y'},
18530            {name: 'value', type: 'float'}
18531         ])
18532     });
18533
18534     var grid = new Ext.grid.GridPanel({
18535         title: 'Invoice Report',
18536         bbar: new Ext.PagingToolbar(store),
18537         store: store,
18538         columns: [
18539             {header: "Customer", width: 250, dataIndex: 'customer', sortable: true},
18540             {header: "Invoice Number", width: 120, dataIndex: 'invNo', sortable: true},
18541             {header: "Invoice Date", width: 100, dataIndex: 'date', renderer: formatDate, sortable: true},
18542             {header: "Value", width: 120, dataIndex: 'value', renderer: 'usMoney', sortable: true}
18543         ],
18544     });
18545     store.load();
18546     return grid;  // return instantiated component
18547 })();
18548 </code></pre>
18549  * <p>When the above code fragment is passed through the <code>eval</code> function in the success handler
18550  * of the Ajax request, the code is executed by the Javascript processor, and the anonymous function
18551  * runs, and returns the instantiated grid component.</p>
18552  * <p>Note: since the code above is <i>generated</i> by a server script, the <code>baseParams</code> for
18553  * the Store, the metadata to allow generation of the Record layout, and the ColumnModel
18554  * can all be generated into the code since these are all known on the server.</p>
18555  *
18556  * @xtype container
18557  */
18558 Ext.Container = Ext.extend(Ext.BoxComponent, {
18559     /**
18560      * @cfg {Boolean} monitorResize
18561      * True to automatically monitor window resize events to handle anything that is sensitive to the current size
18562      * of the viewport.  This value is typically managed by the chosen <code>{@link #layout}</code> and should not need
18563      * to be set manually.
18564      */
18565     /**
18566      * @cfg {String/Object} layout
18567      * <p><b>*Important</b>: In order for child items to be correctly sized and
18568      * positioned, typically a layout manager <b>must</b> be specified through
18569      * the <code>layout</code> configuration option.</p>
18570      * <br><p>The sizing and positioning of child {@link items} is the responsibility of
18571      * the Container's layout manager which creates and manages the type of layout
18572      * you have in mind.  For example:</p><pre><code>
18573 new Ext.Window({
18574     width:300, height: 300,
18575     layout: 'fit', // explicitly set layout manager: override the default (layout:'auto')
18576     items: [{
18577         title: 'Panel inside a Window'
18578     }]
18579 }).show();
18580      * </code></pre>
18581      * <p>If the {@link #layout} configuration is not explicitly specified for
18582      * a general purpose container (e.g. Container or Panel) the
18583      * {@link Ext.layout.ContainerLayout default layout manager} will be used
18584      * which does nothing but render child components sequentially into the
18585      * Container (no sizing or positioning will be performed in this situation).
18586      * Some container classes implicitly specify a default layout
18587      * (e.g. FormPanel specifies <code>layout:'form'</code>). Other specific
18588      * purpose classes internally specify/manage their internal layout (e.g.
18589      * GridPanel, TabPanel, TreePanel, Toolbar, Menu, etc.).</p>
18590      * <br><p><b><code>layout</code></b> may be specified as either as an Object or
18591      * as a String:</p><div><ul class="mdetail-params">
18592      *
18593      * <li><u>Specify as an Object</u></li>
18594      * <div><ul class="mdetail-params">
18595      * <li>Example usage:</li>
18596 <pre><code>
18597 layout: {
18598     type: 'vbox',
18599     padding: '5',
18600     align: 'left'
18601 }
18602 </code></pre>
18603      *
18604      * <li><code><b>type</b></code></li>
18605      * <br/><p>The layout type to be used for this container.  If not specified,
18606      * a default {@link Ext.layout.ContainerLayout} will be created and used.</p>
18607      * <br/><p>Valid layout <code>type</code> values are:</p>
18608      * <div class="sub-desc"><ul class="mdetail-params">
18609      * <li><code><b>{@link Ext.layout.AbsoluteLayout absolute}</b></code></li>
18610      * <li><code><b>{@link Ext.layout.AccordionLayout accordion}</b></code></li>
18611      * <li><code><b>{@link Ext.layout.AnchorLayout anchor}</b></code></li>
18612      * <li><code><b>{@link Ext.layout.ContainerLayout auto}</b></code> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
18613      * <li><code><b>{@link Ext.layout.BorderLayout border}</b></code></li>
18614      * <li><code><b>{@link Ext.layout.CardLayout card}</b></code></li>
18615      * <li><code><b>{@link Ext.layout.ColumnLayout column}</b></code></li>
18616      * <li><code><b>{@link Ext.layout.FitLayout fit}</b></code></li>
18617      * <li><code><b>{@link Ext.layout.FormLayout form}</b></code></li>
18618      * <li><code><b>{@link Ext.layout.HBoxLayout hbox}</b></code></li>
18619      * <li><code><b>{@link Ext.layout.MenuLayout menu}</b></code></li>
18620      * <li><code><b>{@link Ext.layout.TableLayout table}</b></code></li>
18621      * <li><code><b>{@link Ext.layout.ToolbarLayout toolbar}</b></code></li>
18622      * <li><code><b>{@link Ext.layout.VBoxLayout vbox}</b></code></li>
18623      * </ul></div>
18624      *
18625      * <li>Layout specific configuration properties</li>
18626      * <br/><p>Additional layout specific configuration properties may also be
18627      * specified. For complete details regarding the valid config options for
18628      * each layout type, see the layout class corresponding to the <code>type</code>
18629      * specified.</p>
18630      *
18631      * </ul></div>
18632      *
18633      * <li><u>Specify as a String</u></li>
18634      * <div><ul class="mdetail-params">
18635      * <li>Example usage:</li>
18636 <pre><code>
18637 layout: 'vbox',
18638 layoutConfig: {
18639     padding: '5',
18640     align: 'left'
18641 }
18642 </code></pre>
18643      * <li><code><b>layout</b></code></li>
18644      * <br/><p>The layout <code>type</code> to be used for this container (see list
18645      * of valid layout type values above).</p><br/>
18646      * <li><code><b>{@link #layoutConfig}</b></code></li>
18647      * <br/><p>Additional layout specific configuration properties. For complete
18648      * details regarding the valid config options for each layout type, see the
18649      * layout class corresponding to the <code>layout</code> specified.</p>
18650      * </ul></div></ul></div>
18651      */
18652     /**
18653      * @cfg {Object} layoutConfig
18654      * This is a config object containing properties specific to the chosen
18655      * <b><code>{@link #layout}</code></b> if <b><code>{@link #layout}</code></b>
18656      * has been specified as a <i>string</i>.</p>
18657      */
18658     /**
18659      * @cfg {Boolean/Number} bufferResize
18660      * When set to true (50 milliseconds) or a number of milliseconds, the layout assigned for this container will buffer
18661      * the frequency it calculates and does a re-layout of components. This is useful for heavy containers or containers
18662      * with a large quantity of sub-components for which frequent layout calls would be expensive. Defaults to <code>50</code>.
18663      */
18664     bufferResize: 50,
18665
18666     /**
18667      * @cfg {String/Number} activeItem
18668      * A string component id or the numeric index of the component that should be initially activated within the
18669      * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
18670      * item in the container's collection).  activeItem only applies to layout styles that can display
18671      * items one at a time (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout} and
18672      * {@link Ext.layout.FitLayout}).  Related to {@link Ext.layout.ContainerLayout#activeItem}.
18673      */
18674     /**
18675      * @cfg {Object/Array} items
18676      * <pre><b>** IMPORTANT</b>: be sure to <b>{@link #layout specify a <code>layout</code>} if needed ! **</b></pre>
18677      * <p>A single item, or an array of child Components to be added to this container,
18678      * for example:</p>
18679      * <pre><code>
18680 // specifying a single item
18681 items: {...},
18682 layout: 'fit',    // specify a layout!
18683
18684 // specifying multiple items
18685 items: [{...}, {...}],
18686 layout: 'anchor', // specify a layout!
18687      * </code></pre>
18688      * <p>Each item may be:</p>
18689      * <div><ul class="mdetail-params">
18690      * <li>any type of object based on {@link Ext.Component}</li>
18691      * <li>a fully instanciated object or</li>
18692      * <li>an object literal that:</li>
18693      * <div><ul class="mdetail-params">
18694      * <li>has a specified <code>{@link Ext.Component#xtype xtype}</code></li>
18695      * <li>the {@link Ext.Component#xtype} specified is associated with the Component
18696      * desired and should be chosen from one of the available xtypes as listed
18697      * in {@link Ext.Component}.</li>
18698      * <li>If an <code>{@link Ext.Component#xtype xtype}</code> is not explicitly
18699      * specified, the {@link #defaultType} for that Container is used.</li>
18700      * <li>will be "lazily instanciated", avoiding the overhead of constructing a fully
18701      * instanciated Component object</li>
18702      * </ul></div></ul></div>
18703      * <p><b>Notes</b>:</p>
18704      * <div><ul class="mdetail-params">
18705      * <li>Ext uses lazy rendering. Child Components will only be rendered
18706      * should it become necessary. Items are automatically laid out when they are first
18707      * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</li>
18708      * <li>Do not specify <code>{@link Ext.Panel#contentEl contentEl}</code>/
18709      * <code>{@link Ext.Panel#html html}</code> with <code>items</code>.</li>
18710      * </ul></div>
18711      */
18712     /**
18713      * @cfg {Object|Function} defaults
18714      * <p>This option is a means of applying default settings to all added items whether added through the {@link #items}
18715      * config or via the {@link #add} or {@link #insert} methods.</p>
18716      * <p>If an added item is a config object, and <b>not</b> an instantiated Component, then the default properties are
18717      * unconditionally applied. If the added item <b>is</b> an instantiated Component, then the default properties are
18718      * applied conditionally so as not to override existing properties in the item.</p>
18719      * <p>If the defaults option is specified as a function, then the function will be called using this Container as the
18720      * scope (<code>this</code> reference) and passing the added item as the first parameter. Any resulting object
18721      * from that call is then applied to the item as default properties.</p>
18722      * <p>For example, to automatically apply padding to the body of each of a set of
18723      * contained {@link Ext.Panel} items, you could pass: <code>defaults: {bodyStyle:'padding:15px'}</code>.</p>
18724      * <p>Usage:</p><pre><code>
18725 defaults: {               // defaults are applied to items, not the container
18726     autoScroll:true
18727 },
18728 items: [
18729     {
18730         xtype: 'panel',   // defaults <b>do not</b> have precedence over
18731         id: 'panel1',     // options in config objects, so the defaults
18732         autoScroll: false // will not be applied here, panel1 will be autoScroll:false
18733     },
18734     new Ext.Panel({       // defaults <b>do</b> have precedence over options
18735         id: 'panel2',     // options in components, so the defaults
18736         autoScroll: false // will be applied here, panel2 will be autoScroll:true.
18737     })
18738 ]
18739      * </code></pre>
18740      */
18741
18742
18743     /** @cfg {Boolean} autoDestroy
18744      * If true the container will automatically destroy any contained component that is removed from it, else
18745      * destruction must be handled manually (defaults to true).
18746      */
18747     autoDestroy : true,
18748
18749     /** @cfg {Boolean} forceLayout
18750      * If true the container will force a layout initially even if hidden or collapsed. This option
18751      * is useful for forcing forms to render in collapsed or hidden containers. (defaults to false).
18752      */
18753     forceLayout: false,
18754
18755     /** @cfg {Boolean} hideBorders
18756      * True to hide the borders of each contained component, false to defer to the component's existing
18757      * border settings (defaults to false).
18758      */
18759     /** @cfg {String} defaultType
18760      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
18761      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
18762      * <p>Defaults to <code>'panel'</code>, except {@link Ext.menu.Menu} which defaults to <code>'menuitem'</code>,
18763      * and {@link Ext.Toolbar} and {@link Ext.ButtonGroup} which default to <code>'button'</code>.</p>
18764      */
18765     defaultType : 'panel',
18766
18767     /** @cfg {String} resizeEvent
18768      * The event to listen to for resizing in layouts. Defaults to <code>'resize'</code>.
18769      */
18770     resizeEvent: 'resize',
18771
18772     /**
18773      * @cfg {Array} bubbleEvents
18774      * <p>An array of events that, when fired, should be bubbled to any parent container.
18775      * See {@link Ext.util.Observable#enableBubble}.
18776      * Defaults to <code>['add', 'remove']</code>.
18777      */
18778     bubbleEvents: ['add', 'remove'],
18779
18780     // private
18781     initComponent : function(){
18782         Ext.Container.superclass.initComponent.call(this);
18783
18784         this.addEvents(
18785             /**
18786              * @event afterlayout
18787              * Fires when the components in this container are arranged by the associated layout manager.
18788              * @param {Ext.Container} this
18789              * @param {ContainerLayout} layout The ContainerLayout implementation for this container
18790              */
18791             'afterlayout',
18792             /**
18793              * @event beforeadd
18794              * Fires before any {@link Ext.Component} is added or inserted into the container.
18795              * A handler can return false to cancel the add.
18796              * @param {Ext.Container} this
18797              * @param {Ext.Component} component The component being added
18798              * @param {Number} index The index at which the component will be added to the container's items collection
18799              */
18800             'beforeadd',
18801             /**
18802              * @event beforeremove
18803              * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
18804              * false to cancel the remove.
18805              * @param {Ext.Container} this
18806              * @param {Ext.Component} component The component being removed
18807              */
18808             'beforeremove',
18809             /**
18810              * @event add
18811              * @bubbles
18812              * Fires after any {@link Ext.Component} is added or inserted into the container.
18813              * @param {Ext.Container} this
18814              * @param {Ext.Component} component The component that was added
18815              * @param {Number} index The index at which the component was added to the container's items collection
18816              */
18817             'add',
18818             /**
18819              * @event remove
18820              * @bubbles
18821              * Fires after any {@link Ext.Component} is removed from the container.
18822              * @param {Ext.Container} this
18823              * @param {Ext.Component} component The component that was removed
18824              */
18825             'remove'
18826         );
18827
18828         /**
18829          * The collection of components in this container as a {@link Ext.util.MixedCollection}
18830          * @type MixedCollection
18831          * @property items
18832          */
18833         var items = this.items;
18834         if(items){
18835             delete this.items;
18836             this.add(items);
18837         }
18838     },
18839
18840     // private
18841     initItems : function(){
18842         if(!this.items){
18843             this.items = new Ext.util.MixedCollection(false, this.getComponentId);
18844             this.getLayout(); // initialize the layout
18845         }
18846     },
18847
18848     // private
18849     setLayout : function(layout){
18850         if(this.layout && this.layout != layout){
18851             this.layout.setContainer(null);
18852         }
18853         this.layout = layout;
18854         this.initItems();
18855         layout.setContainer(this);
18856     },
18857
18858     afterRender: function(){
18859         // Render this Container, this should be done before setLayout is called which
18860         // will hook onResize
18861         Ext.Container.superclass.afterRender.call(this);
18862         if(!this.layout){
18863             this.layout = 'auto';
18864         }
18865         if(Ext.isObject(this.layout) && !this.layout.layout){
18866             this.layoutConfig = this.layout;
18867             this.layout = this.layoutConfig.type;
18868         }
18869         if(Ext.isString(this.layout)){
18870             this.layout = new Ext.Container.LAYOUTS[this.layout.toLowerCase()](this.layoutConfig);
18871         }
18872         this.setLayout(this.layout);
18873
18874         // If a CardLayout, the active item set
18875         if(this.activeItem !== undefined){
18876             var item = this.activeItem;
18877             delete this.activeItem;
18878             this.layout.setActiveItem(item);
18879         }
18880
18881         // If we have no ownerCt, render and size all children
18882         if(!this.ownerCt){
18883             this.doLayout(false, true);
18884         }
18885
18886         // This is a manually configured flag set by users in conjunction with renderTo.
18887         // Not to be confused with the flag by the same name used in Layouts.
18888         if(this.monitorResize === true){
18889             Ext.EventManager.onWindowResize(this.doLayout, this, [false]);
18890         }
18891     },
18892
18893     /**
18894      * <p>Returns the Element to be used to contain the child Components of this Container.</p>
18895      * <p>An implementation is provided which returns the Container's {@link #getEl Element}, but
18896      * if there is a more complex structure to a Container, this may be overridden to return
18897      * the element into which the {@link #layout layout} renders child Components.</p>
18898      * @return {Ext.Element} The Element to render child Components into.
18899      */
18900     getLayoutTarget : function(){
18901         return this.el;
18902     },
18903
18904     // private - used as the key lookup function for the items collection
18905     getComponentId : function(comp){
18906         return comp.getItemId();
18907     },
18908
18909     /**
18910      * <p>Adds {@link Ext.Component Component}(s) to this Container.</p>
18911      * <br><p><b>Description</b></u> :
18912      * <div><ul class="mdetail-params">
18913      * <li>Fires the {@link #beforeadd} event before adding</li>
18914      * <li>The Container's {@link #defaults default config values} will be applied
18915      * accordingly (see <code>{@link #defaults}</code> for details).</li>
18916      * <li>Fires the {@link #add} event after the component has been added.</li>
18917      * </ul></div>
18918      * <br><p><b>Notes</b></u> :
18919      * <div><ul class="mdetail-params">
18920      * <li>If the Container is <i>already rendered</i> when <code>add</code>
18921      * is called, you may need to call {@link #doLayout} to refresh the view which causes
18922      * any unrendered child Components to be rendered. This is required so that you can
18923      * <code>add</code> multiple child components if needed while only refreshing the layout
18924      * once. For example:<pre><code>
18925 var tb = new {@link Ext.Toolbar}();
18926 tb.render(document.body);  // toolbar is rendered
18927 tb.add({text:'Button 1'}); // add multiple items ({@link #defaultType} for {@link Ext.Toolbar Toolbar} is 'button')
18928 tb.add({text:'Button 2'});
18929 tb.{@link #doLayout}();             // refresh the layout
18930      * </code></pre></li>
18931      * <li><i>Warning:</i> Containers directly managed by the BorderLayout layout manager
18932      * may not be removed or added.  See the Notes for {@link Ext.layout.BorderLayout BorderLayout}
18933      * for more details.</li>
18934      * </ul></div>
18935      * @param {...Object/Array} component
18936      * <p>Either one or more Components to add or an Array of Components to add.  See
18937      * <code>{@link #items}</code> for additional information.</p>
18938      * @return {Ext.Component/Array} The Components that were added.
18939      */
18940     add : function(comp){
18941         this.initItems();
18942         var args = arguments.length > 1;
18943         if(args || Ext.isArray(comp)){
18944             var result = [];
18945             Ext.each(args ? arguments : comp, function(c){
18946                 result.push(this.add(c));
18947             }, this);
18948             return result;
18949         }
18950         var c = this.lookupComponent(this.applyDefaults(comp));
18951         var index = this.items.length;
18952         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
18953             this.items.add(c);
18954             // *onAdded
18955             c.onAdded(this, index);
18956             this.onAdd(c);
18957             this.fireEvent('add', this, c, index);
18958         }
18959         return c;
18960     },
18961
18962     onAdd : function(c){
18963         // Empty template method
18964     },
18965
18966     // private
18967     onAdded : function(container, pos) {
18968         //overridden here so we can cascade down, not worth creating a template method.
18969         this.ownerCt = container;
18970         this.initRef();
18971         //initialize references for child items
18972         this.cascade(function(c){
18973             c.initRef();
18974         });
18975         this.fireEvent('added', this, container, pos);
18976     },
18977
18978     /**
18979      * Inserts a Component into this Container at a specified index. Fires the
18980      * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
18981      * Component has been inserted.
18982      * @param {Number} index The index at which the Component will be inserted
18983      * into the Container's items collection
18984      * @param {Ext.Component} component The child Component to insert.<br><br>
18985      * Ext uses lazy rendering, and will only render the inserted Component should
18986      * it become necessary.<br><br>
18987      * A Component config object may be passed in order to avoid the overhead of
18988      * constructing a real Component object if lazy rendering might mean that the
18989      * inserted Component will not be rendered immediately. To take advantage of
18990      * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
18991      * property to the registered type of the Component wanted.<br><br>
18992      * For a list of all available xtypes, see {@link Ext.Component}.
18993      * @return {Ext.Component} component The Component (or config object) that was
18994      * inserted with the Container's default config values applied.
18995      */
18996     insert : function(index, comp){
18997         this.initItems();
18998         var a = arguments, len = a.length;
18999         if(len > 2){
19000             var result = [];
19001             for(var i = len-1; i >= 1; --i) {
19002                 result.push(this.insert(index, a[i]));
19003             }
19004             return result;
19005         }
19006         var c = this.lookupComponent(this.applyDefaults(comp));
19007         index = Math.min(index, this.items.length);
19008         if(this.fireEvent('beforeadd', this, c, index) !== false && this.onBeforeAdd(c) !== false){
19009             if(c.ownerCt == this){
19010                 this.items.remove(c);
19011             }
19012             this.items.insert(index, c);
19013             c.onAdded(this, index);
19014             this.onAdd(c);
19015             this.fireEvent('add', this, c, index);
19016         }
19017         return c;
19018     },
19019
19020     // private
19021     applyDefaults : function(c){
19022         var d = this.defaults;
19023         if(d){
19024             if(Ext.isFunction(d)){
19025                 d = d.call(this, c);
19026             }
19027             if(Ext.isString(c)){
19028                 c = Ext.ComponentMgr.get(c);
19029                 Ext.apply(c, d);
19030             }else if(!c.events){
19031                 Ext.applyIf(c, d);
19032             }else{
19033                 Ext.apply(c, d);
19034             }
19035         }
19036         return c;
19037     },
19038
19039     // private
19040     onBeforeAdd : function(item){
19041         if(item.ownerCt){
19042             item.ownerCt.remove(item, false);
19043         }
19044         if(this.hideBorders === true){
19045             item.border = (item.border === true);
19046         }
19047     },
19048
19049     /**
19050      * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
19051      * the {@link #remove} event after the component has been removed.
19052      * @param {Component/String} component The component reference or id to remove.
19053      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
19054      * Defaults to the value of this Container's {@link #autoDestroy} config.
19055      * @return {Ext.Component} component The Component that was removed.
19056      */
19057     remove : function(comp, autoDestroy){
19058         this.initItems();
19059         var c = this.getComponent(comp);
19060         if(c && this.fireEvent('beforeremove', this, c) !== false){
19061             this.doRemove(c, autoDestroy);
19062             this.fireEvent('remove', this, c);
19063         }
19064         return c;
19065     },
19066
19067     onRemove: function(c){
19068         // Empty template method
19069     },
19070
19071     // private
19072     doRemove: function(c, autoDestroy){
19073         var l = this.layout,
19074             hasLayout = l && this.rendered;
19075
19076         if(hasLayout){
19077             l.onRemove(c);
19078         }
19079         this.items.remove(c);
19080         c.onRemoved();
19081         this.onRemove(c);
19082         if(autoDestroy === true || (autoDestroy !== false && this.autoDestroy)){
19083             c.destroy();
19084         }
19085         if(hasLayout){
19086             l.afterRemove(c);
19087         }
19088     },
19089
19090     /**
19091      * Removes all components from this container.
19092      * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
19093      * Defaults to the value of this Container's {@link #autoDestroy} config.
19094      * @return {Array} Array of the destroyed components
19095      */
19096     removeAll: function(autoDestroy){
19097         this.initItems();
19098         var item, rem = [], items = [];
19099         this.items.each(function(i){
19100             rem.push(i);
19101         });
19102         for (var i = 0, len = rem.length; i < len; ++i){
19103             item = rem[i];
19104             this.remove(item, autoDestroy);
19105             if(item.ownerCt !== this){
19106                 items.push(item);
19107             }
19108         }
19109         return items;
19110     },
19111
19112     /**
19113      * Examines this container's <code>{@link #items}</code> <b>property</b>
19114      * and gets a direct child component of this container.
19115      * @param {String/Number} comp This parameter may be any of the following:
19116      * <div><ul class="mdetail-params">
19117      * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
19118      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
19119      * <li>a <b><code>Number</code></b> : representing the position of the child component
19120      * within the <code>{@link #items}</code> <b>property</b></li>
19121      * </ul></div>
19122      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
19123      * @return Ext.Component The component (if found).
19124      */
19125     getComponent : function(comp){
19126         if(Ext.isObject(comp)){
19127             comp = comp.getItemId();
19128         }
19129         return this.items.get(comp);
19130     },
19131
19132     // private
19133     lookupComponent : function(comp){
19134         if(Ext.isString(comp)){
19135             return Ext.ComponentMgr.get(comp);
19136         }else if(!comp.events){
19137             return this.createComponent(comp);
19138         }
19139         return comp;
19140     },
19141
19142     // private
19143     createComponent : function(config, defaultType){
19144         if (config.render) {
19145             return config;
19146         }
19147         // add in ownerCt at creation time but then immediately
19148         // remove so that onBeforeAdd can handle it
19149         var c = Ext.create(Ext.apply({
19150             ownerCt: this
19151         }, config), defaultType || this.defaultType);
19152         delete c.initialConfig.ownerCt;
19153         delete c.ownerCt;
19154         return c;
19155     },
19156
19157     /**
19158      * @private
19159      * We can only lay out if there is a view area in which to layout.
19160      * display:none on the layout target, *or any of its parent elements* will mean it has no view area.
19161      */
19162     canLayout : function() {
19163         var el = this.getVisibilityEl();
19164         return el && el.dom && !el.isStyle("display", "none");
19165     },
19166
19167     /**
19168      * Force this container's layout to be recalculated. A call to this function is required after adding a new component
19169      * to an already rendered container, or possibly after changing sizing/position properties of child components.
19170      * @param {Boolean} shallow (optional) True to only calc the layout of this component, and let child components auto
19171      * calc layouts as required (defaults to false, which calls doLayout recursively for each subcontainer)
19172      * @param {Boolean} force (optional) True to force a layout to occur, even if the item is hidden.
19173      * @return {Ext.Container} this
19174      */
19175
19176     doLayout : function(shallow, force){
19177         var rendered = this.rendered,
19178             forceLayout = force || this.forceLayout;
19179
19180         if(this.collapsed || !this.canLayout()){
19181             this.deferLayout = this.deferLayout || !shallow;
19182             if(!forceLayout){
19183                 return;
19184             }
19185             shallow = shallow && !this.deferLayout;
19186         } else {
19187             delete this.deferLayout;
19188         }
19189         if(rendered && this.layout){
19190             this.layout.layout();
19191         }
19192         if(shallow !== true && this.items){
19193             var cs = this.items.items;
19194             for(var i = 0, len = cs.length; i < len; i++){
19195                 var c = cs[i];
19196                 if(c.doLayout){
19197                     c.doLayout(false, forceLayout);
19198                 }
19199             }
19200         }
19201         if(rendered){
19202             this.onLayout(shallow, forceLayout);
19203         }
19204         // Initial layout completed
19205         this.hasLayout = true;
19206         delete this.forceLayout;
19207     },
19208
19209     onLayout : Ext.emptyFn,
19210
19211     // private
19212     shouldBufferLayout: function(){
19213         /*
19214          * Returns true if the container should buffer a layout.
19215          * This is true only if the container has previously been laid out
19216          * and has a parent container that is pending a layout.
19217          */
19218         var hl = this.hasLayout;
19219         if(this.ownerCt){
19220             // Only ever buffer if we've laid out the first time and we have one pending.
19221             return hl ? !this.hasLayoutPending() : false;
19222         }
19223         // Never buffer initial layout
19224         return hl;
19225     },
19226
19227     // private
19228     hasLayoutPending: function(){
19229         // Traverse hierarchy to see if any parent container has a pending layout.
19230         var pending = false;
19231         this.ownerCt.bubble(function(c){
19232             if(c.layoutPending){
19233                 pending = true;
19234                 return false;
19235             }
19236         });
19237         return pending;
19238     },
19239
19240     onShow : function(){
19241         // removes css classes that were added to hide
19242         Ext.Container.superclass.onShow.call(this);
19243         // If we were sized during the time we were hidden, layout.
19244         if(Ext.isDefined(this.deferLayout)){
19245             delete this.deferLayout;
19246             this.doLayout(true);
19247         }
19248     },
19249
19250     /**
19251      * Returns the layout currently in use by the container.  If the container does not currently have a layout
19252      * set, a default {@link Ext.layout.ContainerLayout} will be created and set as the container's layout.
19253      * @return {ContainerLayout} layout The container's layout
19254      */
19255     getLayout : function(){
19256         if(!this.layout){
19257             var layout = new Ext.layout.AutoLayout(this.layoutConfig);
19258             this.setLayout(layout);
19259         }
19260         return this.layout;
19261     },
19262
19263     // private
19264     beforeDestroy : function(){
19265         var c;
19266         if(this.items){
19267             while(c = this.items.first()){
19268                 this.doRemove(c, true);
19269             }
19270         }
19271         if(this.monitorResize){
19272             Ext.EventManager.removeResizeListener(this.doLayout, this);
19273         }
19274         Ext.destroy(this.layout);
19275         Ext.Container.superclass.beforeDestroy.call(this);
19276     },
19277
19278     /**
19279      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
19280      * function call will be the scope provided or the current component. The arguments to the function
19281      * will be the args provided or the current component. If the function returns false at any point,
19282      * the bubble is stopped.
19283      * @param {Function} fn The function to call
19284      * @param {Object} scope (optional) The scope of the function (defaults to current node)
19285      * @param {Array} args (optional) The args to call the function with (default to passing the current component)
19286      * @return {Ext.Container} this
19287      */
19288     bubble : function(fn, scope, args){
19289         var p = this;
19290         while(p){
19291             if(fn.apply(scope || p, args || [p]) === false){
19292                 break;
19293             }
19294             p = p.ownerCt;
19295         }
19296         return this;
19297     },
19298
19299     /**
19300      * Cascades down the component/container heirarchy from this component (called first), calling the specified function with
19301      * each component. The scope (<i>this</i>) of
19302      * function call will be the scope provided or the current component. The arguments to the function
19303      * will be the args provided or the current component. If the function returns false at any point,
19304      * the cascade is stopped on that branch.
19305      * @param {Function} fn The function to call
19306      * @param {Object} scope (optional) The scope of the function (defaults to current component)
19307      * @param {Array} args (optional) The args to call the function with (defaults to passing the current component)
19308      * @return {Ext.Container} this
19309      */
19310     cascade : function(fn, scope, args){
19311         if(fn.apply(scope || this, args || [this]) !== false){
19312             if(this.items){
19313                 var cs = this.items.items;
19314                 for(var i = 0, len = cs.length; i < len; i++){
19315                     if(cs[i].cascade){
19316                         cs[i].cascade(fn, scope, args);
19317                     }else{
19318                         fn.apply(scope || cs[i], args || [cs[i]]);
19319                     }
19320                 }
19321             }
19322         }
19323         return this;
19324     },
19325
19326     /**
19327      * Find a component under this container at any level by id
19328      * @param {String} id
19329      * @return Ext.Component
19330      */
19331     findById : function(id){
19332         var m, ct = this;
19333         this.cascade(function(c){
19334             if(ct != c && c.id === id){
19335                 m = c;
19336                 return false;
19337             }
19338         });
19339         return m || null;
19340     },
19341
19342     /**
19343      * Find a component under this container at any level by xtype or class
19344      * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
19345      * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
19346      * the default), or true to check whether this Component is directly of the specified xtype.
19347      * @return {Array} Array of Ext.Components
19348      */
19349     findByType : function(xtype, shallow){
19350         return this.findBy(function(c){
19351             return c.isXType(xtype, shallow);
19352         });
19353     },
19354
19355     /**
19356      * Find a component under this container at any level by property
19357      * @param {String} prop
19358      * @param {String} value
19359      * @return {Array} Array of Ext.Components
19360      */
19361     find : function(prop, value){
19362         return this.findBy(function(c){
19363             return c[prop] === value;
19364         });
19365     },
19366
19367     /**
19368      * Find a component under this container at any level by a custom function. If the passed function returns
19369      * true, the component will be included in the results. The passed function is called with the arguments (component, this container).
19370      * @param {Function} fn The function to call
19371      * @param {Object} scope (optional)
19372      * @return {Array} Array of Ext.Components
19373      */
19374     findBy : function(fn, scope){
19375         var m = [], ct = this;
19376         this.cascade(function(c){
19377             if(ct != c && fn.call(scope || c, c, ct) === true){
19378                 m.push(c);
19379             }
19380         });
19381         return m;
19382     },
19383
19384     /**
19385      * Get a component contained by this container (alias for items.get(key))
19386      * @param {String/Number} key The index or id of the component
19387      * @return {Ext.Component} Ext.Component
19388      */
19389     get : function(key){
19390         return this.items.get(key);
19391     }
19392 });
19393
19394 Ext.Container.LAYOUTS = {};
19395 Ext.reg('container', Ext.Container);
19396 /**
19397  * @class Ext.layout.ContainerLayout
19398  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
19399  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
19400  */
19401 Ext.layout.ContainerLayout = Ext.extend(Object, {
19402     /**
19403      * @cfg {String} extraCls
19404      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
19405      * customized styles to the container or any of its children using standard CSS rules. See
19406      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
19407      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
19408      * which assign a value by default:
19409      * <div class="mdetail-params"><ul>
19410      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
19411      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
19412      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
19413      * </ul></div>
19414      * To configure the above Classes with an extra CSS class append to the default.  For example,
19415      * for ColumnLayout:<pre><code>
19416      * extraCls: 'x-column custom-class'
19417      * </code></pre>
19418      * </p>
19419      */
19420     /**
19421      * @cfg {Boolean} renderHidden
19422      * True to hide each contained item on render (defaults to false).
19423      */
19424
19425     /**
19426      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
19427      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
19428      * </code></pre>
19429      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
19430      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
19431      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
19432      * @type {Ext.Component}
19433      * @property activeItem
19434      */
19435
19436     // private
19437     monitorResize:false,
19438     // private
19439     activeItem : null,
19440
19441     constructor : function(config){
19442         this.id = Ext.id(null, 'ext-layout-');
19443         Ext.apply(this, config);
19444     },
19445
19446     type: 'container',
19447
19448     /* Workaround for how IE measures autoWidth elements.  It prefers bottom-up measurements
19449       whereas other browser prefer top-down.  We will hide all target child elements before we measure and
19450       put them back to get an accurate measurement.
19451     */
19452     IEMeasureHack : function(target, viewFlag) {
19453         var tChildren = target.dom.childNodes, tLen = tChildren.length, c, d = [], e, i, ret;
19454         for (i = 0 ; i < tLen ; i++) {
19455             c = tChildren[i];
19456             e = Ext.get(c);
19457             if (e) {
19458                 d[i] = e.getStyle('display');
19459                 e.setStyle({display: 'none'});
19460             }
19461         }
19462         ret = target ? target.getViewSize(viewFlag) : {};
19463         for (i = 0 ; i < tLen ; i++) {
19464             c = tChildren[i];
19465             e = Ext.get(c);
19466             if (e) {
19467                 e.setStyle({display: d[i]});
19468             }
19469         }
19470         return ret;
19471     },
19472
19473     // Placeholder for the derived layouts
19474     getLayoutTargetSize : Ext.EmptyFn,
19475
19476     // private
19477     layout : function(){
19478         var ct = this.container, target = ct.getLayoutTarget();
19479         if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){
19480             target.addClass(this.targetCls);
19481         }
19482         this.onLayout(ct, target);
19483         ct.fireEvent('afterlayout', ct, this);
19484     },
19485
19486     // private
19487     onLayout : function(ct, target){
19488         this.renderAll(ct, target);
19489     },
19490
19491     // private
19492     isValidParent : function(c, target){
19493         return target && c.getPositionEl().dom.parentNode == (target.dom || target);
19494     },
19495
19496     // private
19497     renderAll : function(ct, target){
19498         var items = ct.items.items, i, c, len = items.length;
19499         for(i = 0; i < len; i++) {
19500             c = items[i];
19501             if(c && (!c.rendered || !this.isValidParent(c, target))){
19502                 this.renderItem(c, i, target);
19503             }
19504         }
19505     },
19506
19507     /**
19508      * @private
19509      * Renders the given Component into the target Element. If the Component is already rendered,
19510      * it is moved to the provided target instead.
19511      * @param {Ext.Component} c The Component to render
19512      * @param {Number} position The position within the target to render the item to
19513      * @param {Ext.Element} target The target Element
19514      */
19515     renderItem : function(c, position, target){
19516         if (c) {
19517             if (!c.rendered) {
19518                 c.render(target, position);
19519                 this.configureItem(c, position);
19520             } else if (!this.isValidParent(c, target)) {
19521                 if (Ext.isNumber(position)) {
19522                     position = target.dom.childNodes[position];
19523                 }
19524                 
19525                 target.dom.insertBefore(c.getPositionEl().dom, position || null);
19526                 c.container = target;
19527                 this.configureItem(c, position);
19528             }
19529         }
19530     },
19531
19532     // private.
19533     // Get all rendered items to lay out.
19534     getRenderedItems: function(ct){
19535         var t = ct.getLayoutTarget(), cti = ct.items.items, len = cti.length, i, c, items = [];
19536         for (i = 0; i < len; i++) {
19537             if((c = cti[i]).rendered && this.isValidParent(c, t)){
19538                 items.push(c);
19539             }
19540         };
19541         return items;
19542     },
19543
19544     /**
19545      * @private
19546      * Applies extraCls and hides the item if renderHidden is true
19547      */
19548     configureItem: function(c, position){
19549         if (this.extraCls) {
19550             var t = c.getPositionEl ? c.getPositionEl() : c;
19551             t.addClass(this.extraCls);
19552         }
19553         
19554         // If we are forcing a layout, do so *before* we hide so elements have height/width
19555         if (c.doLayout && this.forceLayout) {
19556             c.doLayout();
19557         }
19558         if (this.renderHidden && c != this.activeItem) {
19559             c.hide();
19560         }
19561     },
19562
19563     onRemove: function(c){
19564         if(this.activeItem == c){
19565             delete this.activeItem;
19566         }
19567         if(c.rendered && this.extraCls){
19568             var t = c.getPositionEl ? c.getPositionEl() : c;
19569             t.removeClass(this.extraCls);
19570         }
19571     },
19572
19573     afterRemove: function(c){
19574         if(c.removeRestore){
19575             c.removeMode = 'container';
19576             delete c.removeRestore;
19577         }
19578     },
19579
19580     // private
19581     onResize: function(){
19582         var ct = this.container,
19583             b;
19584         if(ct.collapsed){
19585             return;
19586         }
19587         if(b = ct.bufferResize && ct.shouldBufferLayout()){
19588             if(!this.resizeTask){
19589                 this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
19590                 this.resizeBuffer = Ext.isNumber(b) ? b : 50;
19591             }
19592             ct.layoutPending = true;
19593             this.resizeTask.delay(this.resizeBuffer);
19594         }else{
19595             this.runLayout();
19596         }
19597     },
19598
19599     runLayout: function(){
19600         var ct = this.container;
19601         this.layout();
19602         ct.onLayout();
19603         delete ct.layoutPending;
19604     },
19605
19606     // private
19607     setContainer : function(ct){
19608         /**
19609          * This monitorResize flag will be renamed soon as to avoid confusion
19610          * with the Container version which hooks onWindowResize to doLayout
19611          *
19612          * monitorResize flag in this context attaches the resize event between
19613          * a container and it's layout
19614          */
19615         if(this.monitorResize && ct != this.container){
19616             var old = this.container;
19617             if(old){
19618                 old.un(old.resizeEvent, this.onResize, this);
19619             }
19620             if(ct){
19621                 ct.on(ct.resizeEvent, this.onResize, this);
19622             }
19623         }
19624         this.container = ct;
19625     },
19626
19627     /**
19628      * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
19629      * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
19630      * @param {Number|String} v The encoded margins
19631      * @return {Object} An object with margin sizes for top, right, bottom and left
19632      */
19633     parseMargins : function(v){
19634         if (Ext.isNumber(v)) {
19635             v = v.toString();
19636         }
19637         var ms  = v.split(' '),
19638             len = ms.length;
19639             
19640         if (len == 1) {
19641             ms[1] = ms[2] = ms[3] = ms[0];
19642         } else if(len == 2) {
19643             ms[2] = ms[0];
19644             ms[3] = ms[1];
19645         } else if(len == 3) {
19646             ms[3] = ms[1];
19647         }
19648         
19649         return {
19650             top   :parseInt(ms[0], 10) || 0,
19651             right :parseInt(ms[1], 10) || 0,
19652             bottom:parseInt(ms[2], 10) || 0,
19653             left  :parseInt(ms[3], 10) || 0
19654         };
19655     },
19656
19657     /**
19658      * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as
19659      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
19660      * labeled and styled form Field. A default Template is supplied, but this may be
19661      * overriden to create custom field structures. The template processes values returned from
19662      * {@link Ext.layout.FormLayout#getTemplateArgs}.
19663      * @property fieldTpl
19664      * @type Ext.Template
19665      */
19666     fieldTpl: (function() {
19667         var t = new Ext.Template(
19668             '<div class="x-form-item {itemCls}" tabIndex="-1">',
19669                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
19670                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
19671                 '</div><div class="{clearCls}"></div>',
19672             '</div>'
19673         );
19674         t.disableFormats = true;
19675         return t.compile();
19676     })(),
19677
19678     /*
19679      * Destroys this layout. This is a template method that is empty by default, but should be implemented
19680      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
19681      * @protected
19682      */
19683     destroy : function(){
19684         // Stop any buffered layout tasks
19685         if(this.resizeTask && this.resizeTask.cancel){
19686             this.resizeTask.cancel();
19687         }
19688         if(!Ext.isEmpty(this.targetCls)){
19689             var target = this.container.getLayoutTarget();
19690             if(target){
19691                 target.removeClass(this.targetCls);
19692             }
19693         }
19694     }
19695 });/**
19696  * @class Ext.layout.AutoLayout
19697  * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Container} to
19698  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
19699  * a {@link Ext.Container Container}.</tt>.  AutoLayout provides only a passthrough of any layout calls
19700  * to any child containers.</p>
19701  */
19702 Ext.layout.AutoLayout = Ext.extend(Ext.layout.ContainerLayout, {
19703     type: 'auto',
19704
19705     monitorResize: true,
19706
19707     onLayout : function(ct, target){
19708         Ext.layout.AutoLayout.superclass.onLayout.call(this, ct, target);
19709         var cs = this.getRenderedItems(ct), len = cs.length, i, c;
19710         for(i = 0; i < len; i++){
19711             c = cs[i];
19712             if (c.doLayout){
19713                 // Shallow layout children
19714                 c.doLayout(true);
19715             }
19716         }
19717     }
19718 });
19719
19720 Ext.Container.LAYOUTS['auto'] = Ext.layout.AutoLayout;
19721 /**
19722  * @class Ext.layout.FitLayout
19723  * @extends Ext.layout.ContainerLayout
19724  * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
19725  * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.Container#layout}
19726  * config, and should generally not need to be created directly via the new keyword.</p>
19727  * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container
19728  * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has
19729  * multiple panels, only the first one will be displayed.  Example usage:</p>
19730  * <pre><code>
19731 var p = new Ext.Panel({
19732     title: 'Fit Layout',
19733     layout:'fit',
19734     items: {
19735         title: 'Inner Panel',
19736         html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',
19737         border: false
19738     }
19739 });
19740 </code></pre>
19741  */
19742 Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {
19743     // private
19744     monitorResize:true,
19745
19746     type: 'fit',
19747
19748     getLayoutTargetSize : function() {
19749         var target = this.container.getLayoutTarget();
19750         if (!target) {
19751             return {};
19752         }
19753         // Style Sized (scrollbars not included)
19754         return target.getStyleSize();
19755     },
19756
19757     // private
19758     onLayout : function(ct, target){
19759         Ext.layout.FitLayout.superclass.onLayout.call(this, ct, target);
19760         if(!ct.collapsed){
19761             this.setItemSize(this.activeItem || ct.items.itemAt(0), this.getLayoutTargetSize());
19762         }
19763     },
19764
19765     // private
19766     setItemSize : function(item, size){
19767         if(item && size.height > 0){ // display none?
19768             item.setSize(size);
19769         }
19770     }
19771 });
19772 Ext.Container.LAYOUTS['fit'] = Ext.layout.FitLayout;/**
19773  * @class Ext.layout.CardLayout
19774  * @extends Ext.layout.FitLayout
19775  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
19776  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.
19777  * This class is intended to be extended or created via the layout:'card' {@link Ext.Container#layout} config,
19778  * and should generally not need to be created directly via the new keyword.</p>
19779  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,
19780  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of
19781  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,
19782  * so that functionality must be provided by the developer.</p>
19783  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added
19784  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a
19785  * common navigation routine -- for this example, the implementation of that routine has been ommitted since
19786  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a
19787  * completely different implementation.  For serious implementations, a better approach would be to extend
19788  * CardLayout to provide the custom functionality needed.  Example usage:</p>
19789  * <pre><code>
19790 var navHandler = function(direction){
19791     // This routine could contain business logic required to manage the navigation steps.
19792     // It would call setActiveItem as needed, manage navigation button state, handle any
19793     // branching logic that might be required, handle alternate actions like cancellation
19794     // or finalization, etc.  A complete wizard implementation could get pretty
19795     // sophisticated depending on the complexity required, and should probably be
19796     // done as a subclass of CardLayout in a real-world implementation.
19797 };
19798
19799 var card = new Ext.Panel({
19800     title: 'Example Wizard',
19801     layout:'card',
19802     activeItem: 0, // make sure the active item is set on the container config!
19803     bodyStyle: 'padding:15px',
19804     defaults: {
19805         // applied to each contained panel
19806         border:false
19807     },
19808     // just an example of one possible navigation scheme, using buttons
19809     bbar: [
19810         {
19811             id: 'move-prev',
19812             text: 'Back',
19813             handler: navHandler.createDelegate(this, [-1]),
19814             disabled: true
19815         },
19816         '->', // greedy spacer so that the buttons are aligned to each side
19817         {
19818             id: 'move-next',
19819             text: 'Next',
19820             handler: navHandler.createDelegate(this, [1])
19821         }
19822     ],
19823     // the panels (or "cards") within the layout
19824     items: [{
19825         id: 'card-0',
19826         html: '&lt;h1&gt;Welcome to the Wizard!&lt;/h1&gt;&lt;p&gt;Step 1 of 3&lt;/p&gt;'
19827     },{
19828         id: 'card-1',
19829         html: '&lt;p&gt;Step 2 of 3&lt;/p&gt;'
19830     },{
19831         id: 'card-2',
19832         html: '&lt;h1&gt;Congratulations!&lt;/h1&gt;&lt;p&gt;Step 3 of 3 - Complete&lt;/p&gt;'
19833     }]
19834 });
19835 </code></pre>
19836  */
19837 Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {
19838     /**
19839      * @cfg {Boolean} deferredRender
19840      * True to render each contained item at the time it becomes active, false to render all contained items
19841      * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or
19842      * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to
19843      * true might improve performance.
19844      */
19845     deferredRender : false,
19846
19847     /**
19848      * @cfg {Boolean} layoutOnCardChange
19849      * True to force a layout of the active item when the active card is changed. Defaults to false.
19850      */
19851     layoutOnCardChange : false,
19852
19853     /**
19854      * @cfg {Boolean} renderHidden @hide
19855      */
19856     // private
19857     renderHidden : true,
19858
19859     type: 'card',
19860
19861     /**
19862      * Sets the active (visible) item in the layout.
19863      * @param {String/Number} item The string component id or numeric index of the item to activate
19864      */
19865     setActiveItem : function(item){
19866         var ai = this.activeItem,
19867             ct = this.container;
19868         item = ct.getComponent(item);
19869
19870         // Is this a valid, different card?
19871         if(item && ai != item){
19872
19873             // Changing cards, hide the current one
19874             if(ai){
19875                 ai.hide();
19876                 if (ai.hidden !== true) {
19877                     return false;
19878                 }
19879                 ai.fireEvent('deactivate', ai);
19880             }
19881
19882             var layout = item.doLayout && (this.layoutOnCardChange || !item.rendered);
19883
19884             // Change activeItem reference
19885             this.activeItem = item;
19886
19887             // The container is about to get a recursive layout, remove any deferLayout reference
19888             // because it will trigger a redundant layout.
19889             delete item.deferLayout;
19890
19891             // Show the new component
19892             item.show();
19893
19894             this.layout();
19895
19896             if(layout){
19897                 item.doLayout();
19898             }
19899             item.fireEvent('activate', item);
19900         }
19901     },
19902
19903     // private
19904     renderAll : function(ct, target){
19905         if(this.deferredRender){
19906             this.renderItem(this.activeItem, undefined, target);
19907         }else{
19908             Ext.layout.CardLayout.superclass.renderAll.call(this, ct, target);
19909         }
19910     }
19911 });
19912 Ext.Container.LAYOUTS['card'] = Ext.layout.CardLayout;
19913 /**
19914  * @class Ext.layout.AnchorLayout
19915  * @extends Ext.layout.ContainerLayout
19916  * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
19917  * If the container is resized, all anchored items are automatically rerendered according to their
19918  * <b><tt>{@link #anchor}</tt></b> rules.</p>
19919  * <p>This class is intended to be extended or created via the layout:'anchor' {@link Ext.Container#layout}
19920  * config, and should generally not need to be created directly via the new keyword.</p>
19921  * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
19922  * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
19923  * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
19924  * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
19925  * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
19926  * logic if necessary.  For example:</p>
19927  * <pre><code>
19928 var viewport = new Ext.Viewport({
19929     layout:'anchor',
19930     anchorSize: {width:800, height:600},
19931     items:[{
19932         title:'Item 1',
19933         html:'Content 1',
19934         width:800,
19935         anchor:'right 20%'
19936     },{
19937         title:'Item 2',
19938         html:'Content 2',
19939         width:300,
19940         anchor:'50% 30%'
19941     },{
19942         title:'Item 3',
19943         html:'Content 3',
19944         width:600,
19945         anchor:'-100 50%'
19946     }]
19947 });
19948  * </code></pre>
19949  */
19950 Ext.layout.AnchorLayout = Ext.extend(Ext.layout.ContainerLayout, {
19951     /**
19952      * @cfg {String} anchor
19953      * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
19954      * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
19955      *
19956      * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
19957      * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
19958      * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
19959      * The following types of anchor values are supported:<div class="mdetail-params"><ul>
19960      *
19961      * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
19962      * The first anchor is the percentage width that the item should take up within the container, and the
19963      * second is the percentage height.  For example:<pre><code>
19964 // two values specified
19965 anchor: '100% 50%' // render item complete width of the container and
19966                    // 1/2 height of the container
19967 // one value specified
19968 anchor: '100%'     // the width value; the height will default to auto
19969      * </code></pre></div></li>
19970      *
19971      * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">
19972      * This is a raw adjustment where the first anchor is the offset from the right edge of the container,
19973      * and the second is the offset from the bottom edge. For example:<pre><code>
19974 // two values specified
19975 anchor: '-50 -100' // render item the complete width of the container
19976                    // minus 50 pixels and
19977                    // the complete height minus 100 pixels.
19978 // one value specified
19979 anchor: '-50'      // anchor value is assumed to be the right offset value
19980                    // bottom offset will default to 0
19981      * </code></pre></div></li>
19982      *
19983      * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>
19984      * (or <tt>'b'</tt>).<div class="sub-desc">
19985      * Either the container must have a fixed size or an anchorSize config value defined at render time in
19986      * order for these to have any effect.</div></li>
19987      *
19988      * <li><b>Mixed</b> : <div class="sub-desc">
19989      * Anchor values can also be mixed as needed.  For example, to render the width offset from the container
19990      * right edge by 50 pixels and 75% of the container's height use:
19991      * <pre><code>
19992 anchor: '-50 75%'
19993      * </code></pre></div></li>
19994      *
19995      *
19996      * </ul></div>
19997      */
19998
19999     // private
20000     monitorResize : true,
20001
20002     type : 'anchor',
20003
20004     /**
20005      * @cfg {String} defaultAnchor
20006      *
20007      * default anchor for all child container items applied if no anchor or specific width is set on the child item.  Defaults to '100%'.
20008      *
20009      */
20010     defaultAnchor : '100%',
20011
20012     parseAnchorRE : /^(r|right|b|bottom)$/i,
20013
20014     getLayoutTargetSize : function() {
20015         var target = this.container.getLayoutTarget();
20016         if (!target) {
20017             return {};
20018         }
20019         // Style Sized (scrollbars not included)
20020         return target.getStyleSize();
20021     },
20022
20023     // private
20024     onLayout : function(ct, target){
20025         Ext.layout.AnchorLayout.superclass.onLayout.call(this, ct, target);
20026         var size = this.getLayoutTargetSize();
20027
20028         var w = size.width, h = size.height;
20029
20030         if(w < 20 && h < 20){
20031             return;
20032         }
20033
20034         // find the container anchoring size
20035         var aw, ah;
20036         if(ct.anchorSize){
20037             if(typeof ct.anchorSize == 'number'){
20038                 aw = ct.anchorSize;
20039             }else{
20040                 aw = ct.anchorSize.width;
20041                 ah = ct.anchorSize.height;
20042             }
20043         }else{
20044             aw = ct.initialConfig.width;
20045             ah = ct.initialConfig.height;
20046         }
20047
20048         var cs = this.getRenderedItems(ct), len = cs.length, i, c, a, cw, ch, el, vs, boxes = [];
20049         for(i = 0; i < len; i++){
20050             c = cs[i];
20051             el = c.getPositionEl();
20052
20053             // If a child container item has no anchor and no specific width, set the child to the default anchor size
20054             if (!c.anchor && c.items && !Ext.isNumber(c.width) && !(Ext.isIE6 && Ext.isStrict)){
20055                 c.anchor = this.defaultAnchor;
20056             }
20057
20058             if(c.anchor){
20059                 a = c.anchorSpec;
20060                 if(!a){ // cache all anchor values
20061                     vs = c.anchor.split(' ');
20062                     c.anchorSpec = a = {
20063                         right: this.parseAnchor(vs[0], c.initialConfig.width, aw),
20064                         bottom: this.parseAnchor(vs[1], c.initialConfig.height, ah)
20065                     };
20066                 }
20067                 cw = a.right ? this.adjustWidthAnchor(a.right(w) - el.getMargins('lr'), c) : undefined;
20068                 ch = a.bottom ? this.adjustHeightAnchor(a.bottom(h) - el.getMargins('tb'), c) : undefined;
20069
20070                 if(cw || ch){
20071                     boxes.push({
20072                         comp: c,
20073                         width: cw || undefined,
20074                         height: ch || undefined
20075                     });
20076                 }
20077             }
20078         }
20079         for (i = 0, len = boxes.length; i < len; i++) {
20080             c = boxes[i];
20081             c.comp.setSize(c.width, c.height);
20082         }
20083     },
20084
20085     // private
20086     parseAnchor : function(a, start, cstart){
20087         if(a && a != 'none'){
20088             var last;
20089             // standard anchor
20090             if(this.parseAnchorRE.test(a)){
20091                 var diff = cstart - start;
20092                 return function(v){
20093                     if(v !== last){
20094                         last = v;
20095                         return v - diff;
20096                     }
20097                 }
20098             // percentage
20099             }else if(a.indexOf('%') != -1){
20100                 var ratio = parseFloat(a.replace('%', ''))*.01;
20101                 return function(v){
20102                     if(v !== last){
20103                         last = v;
20104                         return Math.floor(v*ratio);
20105                     }
20106                 }
20107             // simple offset adjustment
20108             }else{
20109                 a = parseInt(a, 10);
20110                 if(!isNaN(a)){
20111                     return function(v){
20112                         if(v !== last){
20113                             last = v;
20114                             return v + a;
20115                         }
20116                     }
20117                 }
20118             }
20119         }
20120         return false;
20121     },
20122
20123     // private
20124     adjustWidthAnchor : function(value, comp){
20125         return value;
20126     },
20127
20128     // private
20129     adjustHeightAnchor : function(value, comp){
20130         return value;
20131     }
20132
20133     /**
20134      * @property activeItem
20135      * @hide
20136      */
20137 });
20138 Ext.Container.LAYOUTS['anchor'] = Ext.layout.AnchorLayout;
20139 /**
20140  * @class Ext.layout.ColumnLayout
20141  * @extends Ext.layout.ContainerLayout
20142  * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of
20143  * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.
20144  * This class is intended to be extended or created via the layout:'column' {@link Ext.Container#layout} config,
20145  * and should generally not need to be created directly via the new keyword.</p>
20146  * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
20147  * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The
20148  * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.
20149  * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>
20150  * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.
20151  * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and
20152  * less than 1 (e.g., .25).</p>
20153  * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the
20154  * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none
20155  * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second
20156  * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on
20157  * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space
20158  * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns
20159  * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your
20160  * layout may not render as expected.  Example usage:</p>
20161  * <pre><code>
20162 // All columns are percentages -- they must add up to 1
20163 var p = new Ext.Panel({
20164     title: 'Column Layout - Percentage Only',
20165     layout:'column',
20166     items: [{
20167         title: 'Column 1',
20168         columnWidth: .25
20169     },{
20170         title: 'Column 2',
20171         columnWidth: .6
20172     },{
20173         title: 'Column 3',
20174         columnWidth: .15
20175     }]
20176 });
20177
20178 // Mix of width and columnWidth -- all columnWidth values must add up
20179 // to 1. The first column will take up exactly 120px, and the last two
20180 // columns will fill the remaining container width.
20181 var p = new Ext.Panel({
20182     title: 'Column Layout - Mixed',
20183     layout:'column',
20184     items: [{
20185         title: 'Column 1',
20186         width: 120
20187     },{
20188         title: 'Column 2',
20189         columnWidth: .8
20190     },{
20191         title: 'Column 3',
20192         columnWidth: .2
20193     }]
20194 });
20195 </code></pre>
20196  */
20197 Ext.layout.ColumnLayout = Ext.extend(Ext.layout.ContainerLayout, {
20198     // private
20199     monitorResize:true,
20200
20201     type: 'column',
20202
20203     extraCls: 'x-column',
20204
20205     scrollOffset : 0,
20206
20207     // private
20208
20209     targetCls: 'x-column-layout-ct',
20210
20211     isValidParent : function(c, target){
20212         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
20213     },
20214
20215     getLayoutTargetSize : function() {
20216         var target = this.container.getLayoutTarget(), ret;
20217         if (target) {
20218             ret = target.getViewSize();
20219
20220             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
20221             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
20222             // with getViewSize
20223             if (Ext.isIE && Ext.isStrict && ret.width == 0){
20224                 ret =  target.getStyleSize();
20225             }
20226
20227             ret.width -= target.getPadding('lr');
20228             ret.height -= target.getPadding('tb');
20229         }
20230         return ret;
20231     },
20232
20233     renderAll : function(ct, target) {
20234         if(!this.innerCt){
20235             // the innerCt prevents wrapping and shuffling while
20236             // the container is resizing
20237             this.innerCt = target.createChild({cls:'x-column-inner'});
20238             this.innerCt.createChild({cls:'x-clear'});
20239         }
20240         Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt);
20241     },
20242
20243     // private
20244     onLayout : function(ct, target){
20245         var cs = ct.items.items,
20246             len = cs.length,
20247             c,
20248             i,
20249             m,
20250             margins = [];
20251
20252         this.renderAll(ct, target);
20253
20254         var size = this.getLayoutTargetSize();
20255
20256         if(size.width < 1 && size.height < 1){ // display none?
20257             return;
20258         }
20259
20260         var w = size.width - this.scrollOffset,
20261             h = size.height,
20262             pw = w;
20263
20264         this.innerCt.setWidth(w);
20265
20266         // some columns can be percentages while others are fixed
20267         // so we need to make 2 passes
20268
20269         for(i = 0; i < len; i++){
20270             c = cs[i];
20271             m = c.getPositionEl().getMargins('lr');
20272             margins[i] = m;
20273             if(!c.columnWidth){
20274                 pw -= (c.getWidth() + m);
20275             }
20276         }
20277
20278         pw = pw < 0 ? 0 : pw;
20279
20280         for(i = 0; i < len; i++){
20281             c = cs[i];
20282             m = margins[i];
20283             if(c.columnWidth){
20284                 c.setSize(Math.floor(c.columnWidth * pw) - m);
20285             }
20286         }
20287
20288         // Browsers differ as to when they account for scrollbars.  We need to re-measure to see if the scrollbar
20289         // spaces were accounted for properly.  If not, re-layout.
20290         if (Ext.isIE) {
20291             if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
20292                 var ts = this.getLayoutTargetSize();
20293                 if (ts.width != size.width){
20294                     this.adjustmentPass = true;
20295                     this.onLayout(ct, target);
20296                 }
20297             }
20298         }
20299         delete this.adjustmentPass;
20300     }
20301
20302     /**
20303      * @property activeItem
20304      * @hide
20305      */
20306 });
20307
20308 Ext.Container.LAYOUTS['column'] = Ext.layout.ColumnLayout;
20309 /**
20310  * @class Ext.layout.BorderLayout
20311  * @extends Ext.layout.ContainerLayout
20312  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
20313  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
20314  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
20315  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
20316  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
20317  * {@link Ext.Container#layout} config, and should generally not need to be created directly
20318  * via the new keyword.</p>
20319  * <p>BorderLayout does not have any direct config options (other than inherited ones).
20320  * All configuration options available for customizing the BorderLayout are at the
20321  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
20322  * levels.</p>
20323  * <p>Example usage:</p>
20324  * <pre><code>
20325 var myBorderPanel = new Ext.Panel({
20326     {@link Ext.Component#renderTo renderTo}: document.body,
20327     {@link Ext.BoxComponent#width width}: 700,
20328     {@link Ext.BoxComponent#height height}: 500,
20329     {@link Ext.Panel#title title}: 'Border Layout',
20330     {@link Ext.Container#layout layout}: 'border',
20331     {@link Ext.Container#items items}: [{
20332         {@link Ext.Panel#title title}: 'South Region is resizable',
20333         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
20334         {@link Ext.BoxComponent#height height}: 100,
20335         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
20336         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
20337         {@link Ext.SplitBar#maxSize maxSize}: 150,
20338         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
20339     },{
20340         // xtype: 'panel' implied by default
20341         {@link Ext.Panel#title title}: 'West Region is collapsible',
20342         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
20343         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
20344         {@link Ext.BoxComponent#width width}: 200,
20345         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
20346         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
20347         {@link Ext.Component#id id}: 'west-region-container',
20348         {@link Ext.Container#layout layout}: 'fit',
20349         {@link Ext.Panel#unstyled unstyled}: true
20350     },{
20351         {@link Ext.Panel#title title}: 'Center Region',
20352         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
20353         {@link Ext.Component#xtype xtype}: 'container',
20354         {@link Ext.Container#layout layout}: 'fit',
20355         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
20356     }]
20357 });
20358 </code></pre>
20359  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
20360  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
20361  * The child item in the center region will always be resized to fill the remaining space not used by
20362  * the other regions in the layout.</li>
20363  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
20364  * (an integer representing the number of pixels that the region should take up).</li>
20365  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
20366  * <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
20367  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
20368  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
20369  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
20370  * is added to the west region:
20371  * <div style="margin-left:16px"><pre><code>
20372 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
20373 wrc.{@link Ext.Panel#removeAll removeAll}();
20374 wrc.{@link Ext.Container#add add}({
20375     title: 'Added Panel',
20376     html: 'Some content'
20377 });
20378 wrc.{@link Ext.Container#doLayout doLayout}();
20379  * </code></pre></div>
20380  * </li>
20381  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
20382  * <div style="margin-left:16px"><pre><code>
20383 wr = myBorderPanel.layout.west;
20384  * </code></pre></div>
20385  * </li>
20386  * </ul></div>
20387  */
20388 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
20389     // private
20390     monitorResize:true,
20391     // private
20392     rendered : false,
20393
20394     type: 'border',
20395
20396     targetCls: 'x-border-layout-ct',
20397
20398     getLayoutTargetSize : function() {
20399         var target = this.container.getLayoutTarget();
20400         return target ? target.getViewSize() : {};
20401     },
20402
20403     // private
20404     onLayout : function(ct, target){
20405         var collapsed, i, c, pos, items = ct.items.items, len = items.length;
20406         if(!this.rendered){
20407             collapsed = [];
20408             for(i = 0; i < len; i++) {
20409                 c = items[i];
20410                 pos = c.region;
20411                 if(c.collapsed){
20412                     collapsed.push(c);
20413                 }
20414                 c.collapsed = false;
20415                 if(!c.rendered){
20416                     c.render(target, i);
20417                     c.getPositionEl().addClass('x-border-panel');
20418                 }
20419                 this[pos] = pos != 'center' && c.split ?
20420                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
20421                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
20422                 this[pos].render(target, c);
20423             }
20424             this.rendered = true;
20425         }
20426
20427         var size = this.getLayoutTargetSize();
20428         if(size.width < 20 || size.height < 20){ // display none?
20429             if(collapsed){
20430                 this.restoreCollapsed = collapsed;
20431             }
20432             return;
20433         }else if(this.restoreCollapsed){
20434             collapsed = this.restoreCollapsed;
20435             delete this.restoreCollapsed;
20436         }
20437
20438         var w = size.width, h = size.height,
20439             centerW = w, centerH = h, centerY = 0, centerX = 0,
20440             n = this.north, s = this.south, west = this.west, e = this.east, c = this.center,
20441             b, m, totalWidth, totalHeight;
20442         if(!c && Ext.layout.BorderLayout.WARN !== false){
20443             throw 'No center region defined in BorderLayout ' + ct.id;
20444         }
20445
20446         if(n && n.isVisible()){
20447             b = n.getSize();
20448             m = n.getMargins();
20449             b.width = w - (m.left+m.right);
20450             b.x = m.left;
20451             b.y = m.top;
20452             centerY = b.height + b.y + m.bottom;
20453             centerH -= centerY;
20454             n.applyLayout(b);
20455         }
20456         if(s && s.isVisible()){
20457             b = s.getSize();
20458             m = s.getMargins();
20459             b.width = w - (m.left+m.right);
20460             b.x = m.left;
20461             totalHeight = (b.height + m.top + m.bottom);
20462             b.y = h - totalHeight + m.top;
20463             centerH -= totalHeight;
20464             s.applyLayout(b);
20465         }
20466         if(west && west.isVisible()){
20467             b = west.getSize();
20468             m = west.getMargins();
20469             b.height = centerH - (m.top+m.bottom);
20470             b.x = m.left;
20471             b.y = centerY + m.top;
20472             totalWidth = (b.width + m.left + m.right);
20473             centerX += totalWidth;
20474             centerW -= totalWidth;
20475             west.applyLayout(b);
20476         }
20477         if(e && e.isVisible()){
20478             b = e.getSize();
20479             m = e.getMargins();
20480             b.height = centerH - (m.top+m.bottom);
20481             totalWidth = (b.width + m.left + m.right);
20482             b.x = w - totalWidth + m.left;
20483             b.y = centerY + m.top;
20484             centerW -= totalWidth;
20485             e.applyLayout(b);
20486         }
20487         if(c){
20488             m = c.getMargins();
20489             var centerBox = {
20490                 x: centerX + m.left,
20491                 y: centerY + m.top,
20492                 width: centerW - (m.left+m.right),
20493                 height: centerH - (m.top+m.bottom)
20494             };
20495             c.applyLayout(centerBox);
20496         }
20497         if(collapsed){
20498             for(i = 0, len = collapsed.length; i < len; i++){
20499                 collapsed[i].collapse(false);
20500             }
20501         }
20502         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
20503             target.repaint();
20504         }
20505         // Putting a border layout into an overflowed container is NOT correct and will make a second layout pass necessary.
20506         if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) {
20507             var ts = this.getLayoutTargetSize();
20508             if (ts.width != size.width || ts.height != size.height){
20509                 this.adjustmentPass = true;
20510                 this.onLayout(ct, target);
20511             }
20512         }
20513         delete this.adjustmentPass;
20514     },
20515
20516     destroy: function() {
20517         var r = ['north', 'south', 'east', 'west'], i, region;
20518         for (i = 0; i < r.length; i++) {
20519             region = this[r[i]];
20520             if(region){
20521                 if(region.destroy){
20522                     region.destroy();
20523                 }else if (region.split){
20524                     region.split.destroy(true);
20525                 }
20526             }
20527         }
20528         Ext.layout.BorderLayout.superclass.destroy.call(this);
20529     }
20530
20531     /**
20532      * @property activeItem
20533      * @hide
20534      */
20535 });
20536
20537 /**
20538  * @class Ext.layout.BorderLayout.Region
20539  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
20540  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
20541  * independent of other regions and the containing BorderLayout, and can be any of the
20542  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
20543  * <p>Region size is managed automatically and cannot be changed by the user -- for
20544  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
20545  * @constructor
20546  * Create a new Region.
20547  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
20548  * @param {Object} config The configuration options
20549  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
20550  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
20551  * <b>must have a center region</b> for the primary content -- all other regions are optional.
20552  */
20553 Ext.layout.BorderLayout.Region = function(layout, config, pos){
20554     Ext.apply(this, config);
20555     this.layout = layout;
20556     this.position = pos;
20557     this.state = {};
20558     if(typeof this.margins == 'string'){
20559         this.margins = this.layout.parseMargins(this.margins);
20560     }
20561     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
20562     if(this.collapsible){
20563         if(typeof this.cmargins == 'string'){
20564             this.cmargins = this.layout.parseMargins(this.cmargins);
20565         }
20566         if(this.collapseMode == 'mini' && !this.cmargins){
20567             this.cmargins = {left:0,top:0,right:0,bottom:0};
20568         }else{
20569             this.cmargins = Ext.applyIf(this.cmargins || {},
20570                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
20571         }
20572     }
20573 };
20574
20575 Ext.layout.BorderLayout.Region.prototype = {
20576     /**
20577      * @cfg {Boolean} animFloat
20578      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
20579      * panel that will close again once the user mouses out of that panel (or clicks out if
20580      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
20581      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
20582      */
20583     /**
20584      * @cfg {Boolean} autoHide
20585      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
20586      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
20587      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
20588      * user clicks outside of the panel (defaults to <tt>true</tt>).
20589      */
20590     /**
20591      * @cfg {String} collapseMode
20592      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
20593      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
20594      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
20595      * title bar.</div></li>
20596      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
20597      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
20598      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
20599      * </div></li>
20600      * </ul></div></p>
20601      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
20602      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
20603      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
20604      * <p>See also <tt>{@link #cmargins}</tt>.</p>
20605      */
20606     /**
20607      * @cfg {Object} margins
20608      * An object containing margins to apply to the region when in the expanded state in the
20609      * format:<pre><code>
20610 {
20611     top: (top margin),
20612     right: (right margin),
20613     bottom: (bottom margin),
20614     left: (left margin)
20615 }</code></pre>
20616      * <p>May also be a string containing space-separated, numeric margin values. The order of the
20617      * sides associated with each value matches the way CSS processes margin values:</p>
20618      * <p><div class="mdetail-params"><ul>
20619      * <li>If there is only one value, it applies to all sides.</li>
20620      * <li>If there are two values, the top and bottom borders are set to the first value and the
20621      * right and left are set to the second.</li>
20622      * <li>If there are three values, the top is set to the first value, the left and right are set
20623      * to the second, and the bottom is set to the third.</li>
20624      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
20625      * </ul></div></p>
20626      * <p>Defaults to:</p><pre><code>
20627      * {top:0, right:0, bottom:0, left:0}
20628      * </code></pre>
20629      */
20630     /**
20631      * @cfg {Object} cmargins
20632      * An object containing margins to apply to the region when in the collapsed state in the
20633      * format:<pre><code>
20634 {
20635     top: (top margin),
20636     right: (right margin),
20637     bottom: (bottom margin),
20638     left: (left margin)
20639 }</code></pre>
20640      * <p>May also be a string containing space-separated, numeric margin values. The order of the
20641      * sides associated with each value matches the way CSS processes margin values.</p>
20642      * <p><ul>
20643      * <li>If there is only one value, it applies to all sides.</li>
20644      * <li>If there are two values, the top and bottom borders are set to the first value and the
20645      * right and left are set to the second.</li>
20646      * <li>If there are three values, the top is set to the first value, the left and right are set
20647      * to the second, and the bottom is set to the third.</li>
20648      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
20649      * </ul></p>
20650      */
20651     /**
20652      * @cfg {Boolean} collapsible
20653      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
20654      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
20655      * bar of the region, otherwise the button will not be shown.</p>
20656      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
20657      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
20658      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
20659      */
20660     collapsible : false,
20661     /**
20662      * @cfg {Boolean} split
20663      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and
20664      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
20665      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
20666      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
20667      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
20668      * <li>this configuration option is ignored if <tt>region='center'</tt></li>
20669      * <li>when <tt>split == true</tt>, it is common to specify a
20670      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
20671      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
20672      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
20673      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
20674      * for the collapse tool</tt></li>
20675      * </ul></div>
20676      */
20677     split:false,
20678     /**
20679      * @cfg {Boolean} floatable
20680      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
20681      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
20682      * clicking the expand button to see it again (defaults to <tt>true</tt>).
20683      */
20684     floatable: true,
20685     /**
20686      * @cfg {Number} minWidth
20687      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
20688      * <tt>maxWidth</tt> may also be specified.</p><br>
20689      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
20690      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
20691      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
20692      */
20693     minWidth:50,
20694     /**
20695      * @cfg {Number} minHeight
20696      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
20697      * <tt>maxHeight</tt> may also be specified.</p><br>
20698      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
20699      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
20700      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
20701      */
20702     minHeight:50,
20703
20704     // private
20705     defaultMargins : {left:0,top:0,right:0,bottom:0},
20706     // private
20707     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
20708     // private
20709     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
20710     floatingZIndex: 100,
20711
20712     /**
20713      * True if this region is collapsed. Read-only.
20714      * @type Boolean
20715      * @property
20716      */
20717     isCollapsed : false,
20718
20719     /**
20720      * This region's panel.  Read-only.
20721      * @type Ext.Panel
20722      * @property panel
20723      */
20724     /**
20725      * This region's layout.  Read-only.
20726      * @type Layout
20727      * @property layout
20728      */
20729     /**
20730      * This region's layout position (north, south, east, west or center).  Read-only.
20731      * @type String
20732      * @property position
20733      */
20734
20735     // private
20736     render : function(ct, p){
20737         this.panel = p;
20738         p.el.enableDisplayMode();
20739         this.targetEl = ct;
20740         this.el = p.el;
20741
20742         var gs = p.getState, ps = this.position;
20743         p.getState = function(){
20744             return Ext.apply(gs.call(p) || {}, this.state);
20745         }.createDelegate(this);
20746
20747         if(ps != 'center'){
20748             p.allowQueuedExpand = false;
20749             p.on({
20750                 beforecollapse: this.beforeCollapse,
20751                 collapse: this.onCollapse,
20752                 beforeexpand: this.beforeExpand,
20753                 expand: this.onExpand,
20754                 hide: this.onHide,
20755                 show: this.onShow,
20756                 scope: this
20757             });
20758             if(this.collapsible || this.floatable){
20759                 p.collapseEl = 'el';
20760                 p.slideAnchor = this.getSlideAnchor();
20761             }
20762             if(p.tools && p.tools.toggle){
20763                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
20764                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
20765             }
20766         }
20767     },
20768
20769     // private
20770     getCollapsedEl : function(){
20771         if(!this.collapsedEl){
20772             if(!this.toolTemplate){
20773                 var tt = new Ext.Template(
20774                      '<div class="x-tool x-tool-{id}">&#160;</div>'
20775                 );
20776                 tt.disableFormats = true;
20777                 tt.compile();
20778                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
20779             }
20780             this.collapsedEl = this.targetEl.createChild({
20781                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
20782                 id: this.panel.id + '-xcollapsed'
20783             });
20784             this.collapsedEl.enableDisplayMode('block');
20785
20786             if(this.collapseMode == 'mini'){
20787                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
20788                 this.miniCollapsedEl = this.collapsedEl.createChild({
20789                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
20790                 });
20791                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
20792                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
20793                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
20794             }else {
20795                 if(this.collapsible !== false && !this.hideCollapseTool) {
20796                     var t = this.toolTemplate.append(
20797                             this.collapsedEl.dom,
20798                             {id:'expand-'+this.position}, true);
20799                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
20800                     t.on('click', this.onExpandClick, this, {stopEvent:true});
20801                 }
20802                 if(this.floatable !== false || this.titleCollapse){
20803                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
20804                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
20805                 }
20806             }
20807         }
20808         return this.collapsedEl;
20809     },
20810
20811     // private
20812     onExpandClick : function(e){
20813         if(this.isSlid){
20814             this.panel.expand(false);
20815         }else{
20816             this.panel.expand();
20817         }
20818     },
20819
20820     // private
20821     onCollapseClick : function(e){
20822         this.panel.collapse();
20823     },
20824
20825     // private
20826     beforeCollapse : function(p, animate){
20827         this.lastAnim = animate;
20828         if(this.splitEl){
20829             this.splitEl.hide();
20830         }
20831         this.getCollapsedEl().show();
20832         var el = this.panel.getEl();
20833         this.originalZIndex = el.getStyle('z-index');
20834         el.setStyle('z-index', 100);
20835         this.isCollapsed = true;
20836         this.layout.layout();
20837     },
20838
20839     // private
20840     onCollapse : function(animate){
20841         this.panel.el.setStyle('z-index', 1);
20842         if(this.lastAnim === false || this.panel.animCollapse === false){
20843             this.getCollapsedEl().dom.style.visibility = 'visible';
20844         }else{
20845             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
20846         }
20847         this.state.collapsed = true;
20848         this.panel.saveState();
20849     },
20850
20851     // private
20852     beforeExpand : function(animate){
20853         if(this.isSlid){
20854             this.afterSlideIn();
20855         }
20856         var c = this.getCollapsedEl();
20857         this.el.show();
20858         if(this.position == 'east' || this.position == 'west'){
20859             this.panel.setSize(undefined, c.getHeight());
20860         }else{
20861             this.panel.setSize(c.getWidth(), undefined);
20862         }
20863         c.hide();
20864         c.dom.style.visibility = 'hidden';
20865         this.panel.el.setStyle('z-index', this.floatingZIndex);
20866     },
20867
20868     // private
20869     onExpand : function(){
20870         this.isCollapsed = false;
20871         if(this.splitEl){
20872             this.splitEl.show();
20873         }
20874         this.layout.layout();
20875         this.panel.el.setStyle('z-index', this.originalZIndex);
20876         this.state.collapsed = false;
20877         this.panel.saveState();
20878     },
20879
20880     // private
20881     collapseClick : function(e){
20882         if(this.isSlid){
20883            e.stopPropagation();
20884            this.slideIn();
20885         }else{
20886            e.stopPropagation();
20887            this.slideOut();
20888         }
20889     },
20890
20891     // private
20892     onHide : function(){
20893         if(this.isCollapsed){
20894             this.getCollapsedEl().hide();
20895         }else if(this.splitEl){
20896             this.splitEl.hide();
20897         }
20898     },
20899
20900     // private
20901     onShow : function(){
20902         if(this.isCollapsed){
20903             this.getCollapsedEl().show();
20904         }else if(this.splitEl){
20905             this.splitEl.show();
20906         }
20907     },
20908
20909     /**
20910      * True if this region is currently visible, else false.
20911      * @return {Boolean}
20912      */
20913     isVisible : function(){
20914         return !this.panel.hidden;
20915     },
20916
20917     /**
20918      * Returns the current margins for this region.  If the region is collapsed, the
20919      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
20920      * {@link #margins} value will be returned.
20921      * @return {Object} An object containing the element's margins: <tt>{left: (left
20922      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
20923      */
20924     getMargins : function(){
20925         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
20926     },
20927
20928     /**
20929      * Returns the current size of this region.  If the region is collapsed, the size of the
20930      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
20931      * @return {Object} An object containing the element's size: <tt>{width: (element width),
20932      * height: (element height)}</tt>
20933      */
20934     getSize : function(){
20935         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
20936     },
20937
20938     /**
20939      * Sets the specified panel as the container element for this region.
20940      * @param {Ext.Panel} panel The new panel
20941      */
20942     setPanel : function(panel){
20943         this.panel = panel;
20944     },
20945
20946     /**
20947      * Returns the minimum allowable width for this region.
20948      * @return {Number} The minimum width
20949      */
20950     getMinWidth: function(){
20951         return this.minWidth;
20952     },
20953
20954     /**
20955      * Returns the minimum allowable height for this region.
20956      * @return {Number} The minimum height
20957      */
20958     getMinHeight: function(){
20959         return this.minHeight;
20960     },
20961
20962     // private
20963     applyLayoutCollapsed : function(box){
20964         var ce = this.getCollapsedEl();
20965         ce.setLeftTop(box.x, box.y);
20966         ce.setSize(box.width, box.height);
20967     },
20968
20969     // private
20970     applyLayout : function(box){
20971         if(this.isCollapsed){
20972             this.applyLayoutCollapsed(box);
20973         }else{
20974             this.panel.setPosition(box.x, box.y);
20975             this.panel.setSize(box.width, box.height);
20976         }
20977     },
20978
20979     // private
20980     beforeSlide: function(){
20981         this.panel.beforeEffect();
20982     },
20983
20984     // private
20985     afterSlide : function(){
20986         this.panel.afterEffect();
20987     },
20988
20989     // private
20990     initAutoHide : function(){
20991         if(this.autoHide !== false){
20992             if(!this.autoHideHd){
20993                 this.autoHideSlideTask = new Ext.util.DelayedTask(this.slideIn, this);
20994                 this.autoHideHd = {
20995                     "mouseout": function(e){
20996                         if(!e.within(this.el, true)){
20997                             this.autoHideSlideTask.delay(500);
20998                         }
20999                     },
21000                     "mouseover" : function(e){
21001                         this.autoHideSlideTask.cancel();
21002                     },
21003                     scope : this
21004                 };
21005             }
21006             this.el.on(this.autoHideHd);
21007             this.collapsedEl.on(this.autoHideHd);
21008         }
21009     },
21010
21011     // private
21012     clearAutoHide : function(){
21013         if(this.autoHide !== false){
21014             this.el.un("mouseout", this.autoHideHd.mouseout);
21015             this.el.un("mouseover", this.autoHideHd.mouseover);
21016             this.collapsedEl.un("mouseout", this.autoHideHd.mouseout);
21017             this.collapsedEl.un("mouseover", this.autoHideHd.mouseover);
21018         }
21019     },
21020
21021     // private
21022     clearMonitor : function(){
21023         Ext.getDoc().un("click", this.slideInIf, this);
21024     },
21025
21026     /**
21027      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
21028      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
21029      * are clicked, or the mouse exits the Region.
21030      */
21031     slideOut : function(){
21032         if(this.isSlid || this.el.hasActiveFx()){
21033             return;
21034         }
21035         this.isSlid = true;
21036         var ts = this.panel.tools, dh, pc;
21037         if(ts && ts.toggle){
21038             ts.toggle.hide();
21039         }
21040         this.el.show();
21041
21042         // Temporarily clear the collapsed flag so we can onResize the panel on the slide
21043         pc = this.panel.collapsed;
21044         this.panel.collapsed = false;
21045
21046         if(this.position == 'east' || this.position == 'west'){
21047             // Temporarily clear the deferHeight flag so we can size the height on the slide
21048             dh = this.panel.deferHeight;
21049             this.panel.deferHeight = false;
21050
21051             this.panel.setSize(undefined, this.collapsedEl.getHeight());
21052
21053             // Put the deferHeight flag back after setSize
21054             this.panel.deferHeight = dh;
21055         }else{
21056             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
21057         }
21058
21059         // Put the collapsed flag back after onResize
21060         this.panel.collapsed = pc;
21061
21062         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
21063         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
21064         this.el.setStyle("z-index", this.floatingZIndex+2);
21065         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
21066         if(this.animFloat !== false){
21067             this.beforeSlide();
21068             this.el.slideIn(this.getSlideAnchor(), {
21069                 callback: function(){
21070                     this.afterSlide();
21071                     this.initAutoHide();
21072                     Ext.getDoc().on("click", this.slideInIf, this);
21073                 },
21074                 scope: this,
21075                 block: true
21076             });
21077         }else{
21078             this.initAutoHide();
21079              Ext.getDoc().on("click", this.slideInIf, this);
21080         }
21081     },
21082
21083     // private
21084     afterSlideIn : function(){
21085         this.clearAutoHide();
21086         this.isSlid = false;
21087         this.clearMonitor();
21088         this.el.setStyle("z-index", "");
21089         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
21090         this.el.dom.style.left = this.restoreLT[0];
21091         this.el.dom.style.top = this.restoreLT[1];
21092
21093         var ts = this.panel.tools;
21094         if(ts && ts.toggle){
21095             ts.toggle.show();
21096         }
21097     },
21098
21099     /**
21100      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
21101      * this region back into its collapsed state.
21102      */
21103     slideIn : function(cb){
21104         if(!this.isSlid || this.el.hasActiveFx()){
21105             Ext.callback(cb);
21106             return;
21107         }
21108         this.isSlid = false;
21109         if(this.animFloat !== false){
21110             this.beforeSlide();
21111             this.el.slideOut(this.getSlideAnchor(), {
21112                 callback: function(){
21113                     this.el.hide();
21114                     this.afterSlide();
21115                     this.afterSlideIn();
21116                     Ext.callback(cb);
21117                 },
21118                 scope: this,
21119                 block: true
21120             });
21121         }else{
21122             this.el.hide();
21123             this.afterSlideIn();
21124         }
21125     },
21126
21127     // private
21128     slideInIf : function(e){
21129         if(!e.within(this.el)){
21130             this.slideIn();
21131         }
21132     },
21133
21134     // private
21135     anchors : {
21136         "west" : "left",
21137         "east" : "right",
21138         "north" : "top",
21139         "south" : "bottom"
21140     },
21141
21142     // private
21143     sanchors : {
21144         "west" : "l",
21145         "east" : "r",
21146         "north" : "t",
21147         "south" : "b"
21148     },
21149
21150     // private
21151     canchors : {
21152         "west" : "tl-tr",
21153         "east" : "tr-tl",
21154         "north" : "tl-bl",
21155         "south" : "bl-tl"
21156     },
21157
21158     // private
21159     getAnchor : function(){
21160         return this.anchors[this.position];
21161     },
21162
21163     // private
21164     getCollapseAnchor : function(){
21165         return this.canchors[this.position];
21166     },
21167
21168     // private
21169     getSlideAnchor : function(){
21170         return this.sanchors[this.position];
21171     },
21172
21173     // private
21174     getAlignAdj : function(){
21175         var cm = this.cmargins;
21176         switch(this.position){
21177             case "west":
21178                 return [0, 0];
21179             break;
21180             case "east":
21181                 return [0, 0];
21182             break;
21183             case "north":
21184                 return [0, 0];
21185             break;
21186             case "south":
21187                 return [0, 0];
21188             break;
21189         }
21190     },
21191
21192     // private
21193     getExpandAdj : function(){
21194         var c = this.collapsedEl, cm = this.cmargins;
21195         switch(this.position){
21196             case "west":
21197                 return [-(cm.right+c.getWidth()+cm.left), 0];
21198             break;
21199             case "east":
21200                 return [cm.right+c.getWidth()+cm.left, 0];
21201             break;
21202             case "north":
21203                 return [0, -(cm.top+cm.bottom+c.getHeight())];
21204             break;
21205             case "south":
21206                 return [0, cm.top+cm.bottom+c.getHeight()];
21207             break;
21208         }
21209     },
21210
21211     destroy : function(){
21212         if (this.autoHideSlideTask && this.autoHideSlideTask.cancel){
21213             this.autoHideSlideTask.cancel();
21214         }
21215         Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
21216     }
21217 };
21218
21219 /**
21220  * @class Ext.layout.BorderLayout.SplitRegion
21221  * @extends Ext.layout.BorderLayout.Region
21222  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
21223  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
21224  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
21225  * @constructor
21226  * Create a new SplitRegion.
21227  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
21228  * @param {Object} config The configuration options
21229  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
21230  * BorderLayout must have a center region for the primary content -- all other regions are optional.
21231  */
21232 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
21233     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
21234     // prevent switch
21235     this.applyLayout = this.applyFns[pos];
21236 };
21237
21238 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
21239     /**
21240      * @cfg {Number} tickSize
21241      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
21242      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
21243      */
21244     /**
21245      * @cfg {String} splitTip
21246      * The tooltip to display when the user hovers over a
21247      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
21248      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
21249      * <tt>{@link #useSplitTips} = true</tt>.
21250      */
21251     splitTip : "Drag to resize.",
21252     /**
21253      * @cfg {String} collapsibleSplitTip
21254      * The tooltip to display when the user hovers over a
21255      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
21256      * (defaults to "Drag to resize. Double click to hide."). Only applies if
21257      * <tt>{@link #useSplitTips} = true</tt>.
21258      */
21259     collapsibleSplitTip : "Drag to resize. Double click to hide.",
21260     /**
21261      * @cfg {Boolean} useSplitTips
21262      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
21263      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
21264      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
21265      */
21266     useSplitTips : false,
21267
21268     // private
21269     splitSettings : {
21270         north : {
21271             orientation: Ext.SplitBar.VERTICAL,
21272             placement: Ext.SplitBar.TOP,
21273             maxFn : 'getVMaxSize',
21274             minProp: 'minHeight',
21275             maxProp: 'maxHeight'
21276         },
21277         south : {
21278             orientation: Ext.SplitBar.VERTICAL,
21279             placement: Ext.SplitBar.BOTTOM,
21280             maxFn : 'getVMaxSize',
21281             minProp: 'minHeight',
21282             maxProp: 'maxHeight'
21283         },
21284         east : {
21285             orientation: Ext.SplitBar.HORIZONTAL,
21286             placement: Ext.SplitBar.RIGHT,
21287             maxFn : 'getHMaxSize',
21288             minProp: 'minWidth',
21289             maxProp: 'maxWidth'
21290         },
21291         west : {
21292             orientation: Ext.SplitBar.HORIZONTAL,
21293             placement: Ext.SplitBar.LEFT,
21294             maxFn : 'getHMaxSize',
21295             minProp: 'minWidth',
21296             maxProp: 'maxWidth'
21297         }
21298     },
21299
21300     // private
21301     applyFns : {
21302         west : function(box){
21303             if(this.isCollapsed){
21304                 return this.applyLayoutCollapsed(box);
21305             }
21306             var sd = this.splitEl.dom, s = sd.style;
21307             this.panel.setPosition(box.x, box.y);
21308             var sw = sd.offsetWidth;
21309             s.left = (box.x+box.width-sw)+'px';
21310             s.top = (box.y)+'px';
21311             s.height = Math.max(0, box.height)+'px';
21312             this.panel.setSize(box.width-sw, box.height);
21313         },
21314         east : function(box){
21315             if(this.isCollapsed){
21316                 return this.applyLayoutCollapsed(box);
21317             }
21318             var sd = this.splitEl.dom, s = sd.style;
21319             var sw = sd.offsetWidth;
21320             this.panel.setPosition(box.x+sw, box.y);
21321             s.left = (box.x)+'px';
21322             s.top = (box.y)+'px';
21323             s.height = Math.max(0, box.height)+'px';
21324             this.panel.setSize(box.width-sw, box.height);
21325         },
21326         north : function(box){
21327             if(this.isCollapsed){
21328                 return this.applyLayoutCollapsed(box);
21329             }
21330             var sd = this.splitEl.dom, s = sd.style;
21331             var sh = sd.offsetHeight;
21332             this.panel.setPosition(box.x, box.y);
21333             s.left = (box.x)+'px';
21334             s.top = (box.y+box.height-sh)+'px';
21335             s.width = Math.max(0, box.width)+'px';
21336             this.panel.setSize(box.width, box.height-sh);
21337         },
21338         south : function(box){
21339             if(this.isCollapsed){
21340                 return this.applyLayoutCollapsed(box);
21341             }
21342             var sd = this.splitEl.dom, s = sd.style;
21343             var sh = sd.offsetHeight;
21344             this.panel.setPosition(box.x, box.y+sh);
21345             s.left = (box.x)+'px';
21346             s.top = (box.y)+'px';
21347             s.width = Math.max(0, box.width)+'px';
21348             this.panel.setSize(box.width, box.height-sh);
21349         }
21350     },
21351
21352     // private
21353     render : function(ct, p){
21354         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
21355
21356         var ps = this.position;
21357
21358         this.splitEl = ct.createChild({
21359             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
21360             id: this.panel.id + '-xsplit'
21361         });
21362
21363         if(this.collapseMode == 'mini'){
21364             this.miniSplitEl = this.splitEl.createChild({
21365                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
21366             });
21367             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
21368             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
21369         }
21370
21371         var s = this.splitSettings[ps];
21372
21373         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
21374         this.split.tickSize = this.tickSize;
21375         this.split.placement = s.placement;
21376         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
21377         this.split.minSize = this.minSize || this[s.minProp];
21378         this.split.on("beforeapply", this.onSplitMove, this);
21379         this.split.useShim = this.useShim === true;
21380         this.maxSize = this.maxSize || this[s.maxProp];
21381
21382         if(p.hidden){
21383             this.splitEl.hide();
21384         }
21385
21386         if(this.useSplitTips){
21387             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
21388         }
21389         if(this.collapsible){
21390             this.splitEl.on("dblclick", this.onCollapseClick,  this);
21391         }
21392     },
21393
21394     //docs inherit from superclass
21395     getSize : function(){
21396         if(this.isCollapsed){
21397             return this.collapsedEl.getSize();
21398         }
21399         var s = this.panel.getSize();
21400         if(this.position == 'north' || this.position == 'south'){
21401             s.height += this.splitEl.dom.offsetHeight;
21402         }else{
21403             s.width += this.splitEl.dom.offsetWidth;
21404         }
21405         return s;
21406     },
21407
21408     // private
21409     getHMaxSize : function(){
21410          var cmax = this.maxSize || 10000;
21411          var center = this.layout.center;
21412          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
21413     },
21414
21415     // private
21416     getVMaxSize : function(){
21417         var cmax = this.maxSize || 10000;
21418         var center = this.layout.center;
21419         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
21420     },
21421
21422     // private
21423     onSplitMove : function(split, newSize){
21424         var s = this.panel.getSize();
21425         this.lastSplitSize = newSize;
21426         if(this.position == 'north' || this.position == 'south'){
21427             this.panel.setSize(s.width, newSize);
21428             this.state.height = newSize;
21429         }else{
21430             this.panel.setSize(newSize, s.height);
21431             this.state.width = newSize;
21432         }
21433         this.layout.layout();
21434         this.panel.saveState();
21435         return false;
21436     },
21437
21438     /**
21439      * Returns a reference to the split bar in use by this region.
21440      * @return {Ext.SplitBar} The split bar
21441      */
21442     getSplitBar : function(){
21443         return this.split;
21444     },
21445
21446     // inherit docs
21447     destroy : function() {
21448         Ext.destroy(this.miniSplitEl, this.split, this.splitEl);
21449         Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this);
21450     }
21451 });
21452
21453 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;/**
21454  * @class Ext.layout.FormLayout
21455  * @extends Ext.layout.AnchorLayout
21456  * <p>This layout manager is specifically designed for rendering and managing child Components of
21457  * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of
21458  * {@link Ext.form.Field Field}s.</p>
21459  *
21460  * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt>
21461  * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly
21462  * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
21463  *
21464  * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel}
21465  * (which is configured with FormLayout as its layout class by default) since it also provides built-in
21466  * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p>
21467  *
21468  * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g.
21469  * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following
21470  * layout-specific config properties:<div class="mdetail-params"><ul>
21471  * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li>
21472  * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li>
21473  * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li>
21474  * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li>
21475  * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li>
21476  * </ul></div></p>
21477  *
21478  * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option:
21479  * <div class="mdetail-params"><ul>
21480  * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li>
21481  * </ul></div></p>
21482  *
21483  * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by
21484  * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured
21485  * in this way may be configured with the following options which affect the way the FormLayout renders them:
21486  * <div class="mdetail-params"><ul>
21487  * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li>
21488  * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li>
21489  * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li>
21490  * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li>
21491  * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li>
21492  * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li>
21493  * </ul></div></p>
21494  *
21495  * <p>Example usage:</p>
21496  * <pre><code>
21497 // Required if showing validation messages
21498 Ext.QuickTips.init();
21499
21500 // While you can create a basic Panel with layout:'form', practically
21501 // you should usually use a FormPanel to also get its form functionality
21502 // since it already creates a FormLayout internally.
21503 var form = new Ext.form.FormPanel({
21504     title: 'Form Layout',
21505     bodyStyle: 'padding:15px',
21506     width: 350,
21507     defaultType: 'textfield',
21508     defaults: {
21509         // applied to each contained item
21510         width: 230,
21511         msgTarget: 'side'
21512     },
21513     items: [{
21514             fieldLabel: 'First Name',
21515             name: 'first',
21516             allowBlank: false,
21517             {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config
21518         },{
21519             fieldLabel: 'Last Name',
21520             name: 'last'
21521         },{
21522             fieldLabel: 'Email',
21523             name: 'email',
21524             vtype:'email'
21525         }, {
21526             xtype: 'textarea',
21527             hideLabel: true,     // override hideLabels layout config
21528             name: 'msg',
21529             anchor: '100% -53'
21530         }
21531     ],
21532     buttons: [
21533         {text: 'Save'},
21534         {text: 'Cancel'}
21535     ],
21536     layoutConfig: {
21537         {@link #labelSeparator}: '~' // superseded by assignment below
21538     },
21539     // config options applicable to container when layout='form':
21540     hideLabels: false,
21541     labelAlign: 'left',   // or 'right' or 'top'
21542     {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value
21543     labelWidth: 65,       // defaults to 100
21544     labelPad: 8           // defaults to 5, must specify labelWidth to be honored
21545 });
21546 </code></pre>
21547  */
21548 Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, {
21549
21550     /**
21551      * @cfg {String} labelSeparator
21552      * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}.  Configuration
21553      * of this property at the <b>container</b> level takes precedence.
21554      */
21555     labelSeparator : ':',
21556
21557     /**
21558      * Read only. The CSS style specification string added to field labels in this layout if not
21559      * otherwise {@link Ext.Component#labelStyle specified by each contained field}.
21560      * @type String
21561      * @property labelStyle
21562      */
21563
21564     /**
21565      * @cfg {Boolean} trackLabels
21566      * True to show/hide the field label when the field is hidden. Defaults to <tt>false</tt>.
21567      */
21568     trackLabels: false,
21569
21570     type: 'form',
21571
21572     onRemove: function(c){
21573         Ext.layout.FormLayout.superclass.onRemove.call(this, c);
21574         if(this.trackLabels){
21575             c.un('show', this.onFieldShow, this);
21576             c.un('hide', this.onFieldHide, this);
21577         }
21578         // check for itemCt, since we may be removing a fieldset or something similar
21579         var el = c.getPositionEl(),
21580             ct = c.getItemCt && c.getItemCt();
21581         if (c.rendered && ct) {
21582             if (el && el.dom) {
21583                 el.insertAfter(ct);
21584             }
21585             Ext.destroy(ct);
21586             Ext.destroyMembers(c, 'label', 'itemCt');
21587             if (c.customItemCt) {
21588                 Ext.destroyMembers(c, 'getItemCt', 'customItemCt');
21589             }
21590         }
21591     },
21592
21593     // private
21594     setContainer : function(ct){
21595         Ext.layout.FormLayout.superclass.setContainer.call(this, ct);
21596         if(ct.labelAlign){
21597             ct.addClass('x-form-label-'+ct.labelAlign);
21598         }
21599
21600         if(ct.hideLabels){
21601             Ext.apply(this, {
21602                 labelStyle: 'display:none',
21603                 elementStyle: 'padding-left:0;',
21604                 labelAdjust: 0
21605             });
21606         }else{
21607             this.labelSeparator = ct.labelSeparator || this.labelSeparator;
21608             ct.labelWidth = ct.labelWidth || 100;
21609             if(Ext.isNumber(ct.labelWidth)){
21610                 var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5;
21611                 Ext.apply(this, {
21612                     labelAdjust: ct.labelWidth + pad,
21613                     labelStyle: 'width:' + ct.labelWidth + 'px;',
21614                     elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px'
21615                 });
21616             }
21617             if(ct.labelAlign == 'top'){
21618                 Ext.apply(this, {
21619                     labelStyle: 'width:auto;',
21620                     labelAdjust: 0,
21621                     elementStyle: 'padding-left:0;'
21622                 });
21623             }
21624         }
21625     },
21626
21627     // private
21628     isHide: function(c){
21629         return c.hideLabel || this.container.hideLabels;
21630     },
21631
21632     onFieldShow: function(c){
21633         c.getItemCt().removeClass('x-hide-' + c.hideMode);
21634
21635         // Composite fields will need to layout after the container is made visible
21636         if (c.isComposite) {
21637             c.doLayout();
21638         }
21639     },
21640
21641     onFieldHide: function(c){
21642         c.getItemCt().addClass('x-hide-' + c.hideMode);
21643     },
21644
21645     //private
21646     getLabelStyle: function(s){
21647         var ls = '', items = [this.labelStyle, s];
21648         for (var i = 0, len = items.length; i < len; ++i){
21649             if (items[i]){
21650                 ls += items[i];
21651                 if (ls.substr(-1, 1) != ';'){
21652                     ls += ';';
21653                 }
21654             }
21655         }
21656         return ls;
21657     },
21658
21659     /**
21660      * @cfg {Ext.Template} fieldTpl
21661      * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering
21662      * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code>
21663 new Ext.Template(
21664     &#39;&lt;div class="x-form-item {itemCls}" tabIndex="-1">&#39;,
21665         &#39;&lt;&#108;abel for="{id}" style="{labelStyle}" class="x-form-item-&#108;abel">{&#108;abel}{labelSeparator}&lt;/&#108;abel>&#39;,
21666         &#39;&lt;div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">&#39;,
21667         &#39;&lt;/div>&lt;div class="{clearCls}">&lt;/div>&#39;,
21668     '&lt;/div>'
21669 );
21670 </code></pre>
21671      * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p>
21672      * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul>
21673      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
21674      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
21675      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
21676      * supplied at the container level.</div></li>
21677      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
21678      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
21679      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
21680      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
21681      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
21682      * field (defaults to <tt>''</tt>)</div></li>
21683      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
21684      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
21685      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
21686      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
21687      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
21688      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
21689      * </ul></div>
21690      * <p>Also see <tt>{@link #getTemplateArgs}</tt></p>
21691      */
21692
21693     /**
21694      * @private
21695      *
21696      */
21697     renderItem : function(c, position, target){
21698         if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){
21699             var args = this.getTemplateArgs(c);
21700             if(Ext.isNumber(position)){
21701                 position = target.dom.childNodes[position] || null;
21702             }
21703             if(position){
21704                 c.itemCt = this.fieldTpl.insertBefore(position, args, true);
21705             }else{
21706                 c.itemCt = this.fieldTpl.append(target, args, true);
21707             }
21708             if(!c.getItemCt){
21709                 // Non form fields don't have getItemCt, apply it here
21710                 // This will get cleaned up in onRemove
21711                 Ext.apply(c, {
21712                     getItemCt: function(){
21713                         return c.itemCt;
21714                     },
21715                     customItemCt: true
21716                 });
21717             }
21718             c.label = c.getItemCt().child('label.x-form-item-label');
21719             if(!c.rendered){
21720                 c.render('x-form-el-' + c.id);
21721             }else if(!this.isValidParent(c, target)){
21722                 Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl());
21723             }
21724             if(this.trackLabels){
21725                 if(c.hidden){
21726                     this.onFieldHide(c);
21727                 }
21728                 c.on({
21729                     scope: this,
21730                     show: this.onFieldShow,
21731                     hide: this.onFieldHide
21732                 });
21733             }
21734             this.configureItem(c);
21735         }else {
21736             Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
21737         }
21738     },
21739
21740     /**
21741      * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p>
21742      * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl}
21743      * to create a correctly wrapped, labeled and styled form Field. This may be overriden to
21744      * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul>
21745      * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper
21746      * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt>
21747      * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt>
21748      * supplied at the container level.</div></li>
21749      * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li>
21750      * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc">
21751      * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the
21752      * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li>
21753      * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this
21754      * field (defaults to the field's configured fieldLabel property)</div></li>
21755      * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after
21756      * the text of the label for this field (defaults to a colon <tt>':'</tt> or the
21757      * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li>
21758      * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li>
21759      * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div
21760      * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li>
21761      * </ul></div>
21762      * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered.
21763      * @return {Object} An object hash containing the properties required to render the Field.
21764      */
21765     getTemplateArgs: function(field) {
21766         var noLabelSep = !field.fieldLabel || field.hideLabel;
21767
21768         return {
21769             id            : field.id,
21770             label         : field.fieldLabel,
21771             itemCls       : (field.itemCls || this.container.itemCls || '') + (field.hideLabel ? ' x-hide-label' : ''),
21772             clearCls      : field.clearCls || 'x-form-clear-left',
21773             labelStyle    : this.getLabelStyle(field.labelStyle),
21774             elementStyle  : this.elementStyle || '',
21775             labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator)
21776         };
21777     },
21778
21779     // private
21780     adjustWidthAnchor: function(value, c){
21781         if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){
21782             var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict);
21783             return value - this.labelAdjust + (adjust ? -3 : 0);
21784         }
21785         return value;
21786     },
21787
21788     adjustHeightAnchor : function(value, c){
21789         if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){
21790             return value - c.label.getHeight();
21791         }
21792         return value;
21793     },
21794
21795     // private
21796     isValidParent : function(c, target){
21797         return target && this.container.getEl().contains(c.getPositionEl());
21798     }
21799
21800     /**
21801      * @property activeItem
21802      * @hide
21803      */
21804 });
21805
21806 Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout;
21807 /**
21808  * @class Ext.layout.AccordionLayout
21809  * @extends Ext.layout.FitLayout
21810  * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only
21811  * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>
21812  * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.Panel</b> may be used in an accordion layout Container.</p>
21813  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
21814  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
21815  * <p>Example usage:</p>
21816  * <pre><code>
21817 var accordion = new Ext.Panel({
21818     title: 'Accordion Layout',
21819     layout:'accordion',
21820     defaults: {
21821         // applied to each contained panel
21822         bodyStyle: 'padding:15px'
21823     },
21824     layoutConfig: {
21825         // layout-specific configs go here
21826         titleCollapse: false,
21827         animate: true,
21828         activeOnTop: true
21829     },
21830     items: [{
21831         title: 'Panel 1',
21832         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
21833     },{
21834         title: 'Panel 2',
21835         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
21836     },{
21837         title: 'Panel 3',
21838         html: '&lt;p&gt;Panel content!&lt;/p&gt;'
21839     }]
21840 });
21841 </code></pre>
21842  */
21843 Ext.layout.AccordionLayout = Ext.extend(Ext.layout.FitLayout, {
21844     /**
21845      * @cfg {Boolean} fill
21846      * True to adjust the active item's height to fill the available space in the container, false to use the
21847      * item's current height, or auto height if not explicitly set (defaults to true).
21848      */
21849     fill : true,
21850     /**
21851      * @cfg {Boolean} autoWidth
21852      * True to set each contained item's width to 'auto', false to use the item's current width (defaults to true).
21853      * Note that some components, in particular the {@link Ext.grid.GridPanel grid}, will not function properly within
21854      * layouts if they have auto width, so in such cases this config should be set to false.
21855      */
21856     autoWidth : true,
21857     /**
21858      * @cfg {Boolean} titleCollapse
21859      * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
21860      * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,
21861      * {@link #hideCollapseTool} should be false also.
21862      */
21863     titleCollapse : true,
21864     /**
21865      * @cfg {Boolean} hideCollapseTool
21866      * True to hide the contained panels' collapse/expand toggle buttons, false to display them (defaults to false).
21867      * When set to true, {@link #titleCollapse} should be true also.
21868      */
21869     hideCollapseTool : false,
21870     /**
21871      * @cfg {Boolean} collapseFirst
21872      * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
21873      * in the contained panels' title bars, false to render it last (defaults to false).
21874      */
21875     collapseFirst : false,
21876     /**
21877      * @cfg {Boolean} animate
21878      * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
21879      * close directly with no animation (defaults to false).  Note: to defer to the specific config setting of each
21880      * contained panel for this property, set this to undefined at the layout level.
21881      */
21882     animate : false,
21883     /**
21884      * @cfg {Boolean} sequence
21885      * <b>Experimental</b>. If animate is set to true, this will result in each animation running in sequence.
21886      */
21887     sequence : false,
21888     /**
21889      * @cfg {Boolean} activeOnTop
21890      * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
21891      * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).
21892      */
21893     activeOnTop : false,
21894
21895     type: 'accordion',
21896
21897     renderItem : function(c){
21898         if(this.animate === false){
21899             c.animCollapse = false;
21900         }
21901         c.collapsible = true;
21902         if(this.autoWidth){
21903             c.autoWidth = true;
21904         }
21905         if(this.titleCollapse){
21906             c.titleCollapse = true;
21907         }
21908         if(this.hideCollapseTool){
21909             c.hideCollapseTool = true;
21910         }
21911         if(this.collapseFirst !== undefined){
21912             c.collapseFirst = this.collapseFirst;
21913         }
21914         if(!this.activeItem && !c.collapsed){
21915             this.setActiveItem(c, true);
21916         }else if(this.activeItem && this.activeItem != c){
21917             c.collapsed = true;
21918         }
21919         Ext.layout.AccordionLayout.superclass.renderItem.apply(this, arguments);
21920         c.header.addClass('x-accordion-hd');
21921         c.on('beforeexpand', this.beforeExpand, this);
21922     },
21923
21924     onRemove: function(c){
21925         Ext.layout.AccordionLayout.superclass.onRemove.call(this, c);
21926         if(c.rendered){
21927             c.header.removeClass('x-accordion-hd');
21928         }
21929         c.un('beforeexpand', this.beforeExpand, this);
21930     },
21931
21932     // private
21933     beforeExpand : function(p, anim){
21934         var ai = this.activeItem;
21935         if(ai){
21936             if(this.sequence){
21937                 delete this.activeItem;
21938                 if (!ai.collapsed){
21939                     ai.collapse({callback:function(){
21940                         p.expand(anim || true);
21941                     }, scope: this});
21942                     return false;
21943                 }
21944             }else{
21945                 ai.collapse(this.animate);
21946             }
21947         }
21948         this.setActive(p);
21949         if(this.activeOnTop){
21950             p.el.dom.parentNode.insertBefore(p.el.dom, p.el.dom.parentNode.firstChild);
21951         }
21952         // Items have been hidden an possibly rearranged, we need to get the container size again.
21953         this.layout();
21954     },
21955
21956     // private
21957     setItemSize : function(item, size){
21958         if(this.fill && item){
21959             var hh = 0, i, ct = this.getRenderedItems(this.container), len = ct.length, p;
21960             // Add up all the header heights
21961             for (i = 0; i < len; i++) {
21962                 if((p = ct[i]) != item && !p.hidden){
21963                     hh += p.header.getHeight();
21964                 }
21965             };
21966             // Subtract the header heights from the container size
21967             size.height -= hh;
21968             // Call setSize on the container to set the correct height.  For Panels, deferedHeight
21969             // will simply store this size for when the expansion is done.
21970             item.setSize(size);
21971         }
21972     },
21973
21974     /**
21975      * Sets the active (expanded) item in the layout.
21976      * @param {String/Number} item The string component id or numeric index of the item to activate
21977      */
21978     setActiveItem : function(item){
21979         this.setActive(item, true);
21980     },
21981
21982     // private
21983     setActive : function(item, expand){
21984         var ai = this.activeItem;
21985         item = this.container.getComponent(item);
21986         if(ai != item){
21987             if(item.rendered && item.collapsed && expand){
21988                 item.expand();
21989             }else{
21990                 if(ai){
21991                    ai.fireEvent('deactivate', ai);
21992                 }
21993                 this.activeItem = item;
21994                 item.fireEvent('activate', item);
21995             }
21996         }
21997     }
21998 });
21999 Ext.Container.LAYOUTS.accordion = Ext.layout.AccordionLayout;
22000
22001 //backwards compat
22002 Ext.layout.Accordion = Ext.layout.AccordionLayout;/**
22003  * @class Ext.layout.TableLayout
22004  * @extends Ext.layout.ContainerLayout
22005  * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be
22006  * specified, and rowspan and colspan can be used to create complex layouts within the table.
22007  * This class is intended to be extended or created via the layout:'table' {@link Ext.Container#layout} config,
22008  * and should generally not need to be created directly via the new keyword.</p>
22009  * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via
22010  * the {@link Ext.Container#layoutConfig} object which will then be applied internally to the layout.  In the
22011  * case of TableLayout, the only valid layout config property is {@link #columns}.  However, the items added to a
22012  * TableLayout can supply the following table-specific config properties:</p>
22013  * <ul>
22014  * <li><b>rowspan</b> Applied to the table cell containing the item.</li>
22015  * <li><b>colspan</b> Applied to the table cell containing the item.</li>
22016  * <li><b>cellId</b> An id applied to the table cell containing the item.</li>
22017  * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>
22018  * </ul>
22019  * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard
22020  * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes
22021  * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
22022  * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
22023  * total column count in the layoutConfig and start adding panels in their natural order from left to right,
22024  * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,
22025  * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add
22026  * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>
22027  * <pre><code>
22028 // This code will generate a layout table that is 3 columns by 2 rows
22029 // with some spanning included.  The basic layout will be:
22030 // +--------+-----------------+
22031 // |   A    |   B             |
22032 // |        |--------+--------|
22033 // |        |   C    |   D    |
22034 // +--------+--------+--------+
22035 var table = new Ext.Panel({
22036     title: 'Table Layout',
22037     layout:'table',
22038     defaults: {
22039         // applied to each contained panel
22040         bodyStyle:'padding:20px'
22041     },
22042     layoutConfig: {
22043         // The total column count must be specified here
22044         columns: 3
22045     },
22046     items: [{
22047         html: '&lt;p&gt;Cell A content&lt;/p&gt;',
22048         rowspan: 2
22049     },{
22050         html: '&lt;p&gt;Cell B content&lt;/p&gt;',
22051         colspan: 2
22052     },{
22053         html: '&lt;p&gt;Cell C content&lt;/p&gt;',
22054         cellCls: 'highlight'
22055     },{
22056         html: '&lt;p&gt;Cell D content&lt;/p&gt;'
22057     }]
22058 });
22059 </code></pre>
22060  */
22061 Ext.layout.TableLayout = Ext.extend(Ext.layout.ContainerLayout, {
22062     /**
22063      * @cfg {Number} columns
22064      * The total number of columns to create in the table for this layout.  If not specified, all Components added to
22065      * this layout will be rendered into a single row using one column per Component.
22066      */
22067
22068     // private
22069     monitorResize:false,
22070
22071     type: 'table',
22072
22073     targetCls: 'x-table-layout-ct',
22074
22075     /**
22076      * @cfg {Object} tableAttrs
22077      * <p>An object containing properties which are added to the {@link Ext.DomHelper DomHelper} specification
22078      * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>
22079 {
22080     xtype: 'panel',
22081     layout: 'table',
22082     layoutConfig: {
22083         tableAttrs: {
22084             style: {
22085                 width: '100%'
22086             }
22087         },
22088         columns: 3
22089     }
22090 }</code></pre>
22091      */
22092     tableAttrs:null,
22093
22094     // private
22095     setContainer : function(ct){
22096         Ext.layout.TableLayout.superclass.setContainer.call(this, ct);
22097
22098         this.currentRow = 0;
22099         this.currentColumn = 0;
22100         this.cells = [];
22101     },
22102     
22103     // private
22104     onLayout : function(ct, target){
22105         var cs = ct.items.items, len = cs.length, c, i;
22106
22107         if(!this.table){
22108             target.addClass('x-table-layout-ct');
22109
22110             this.table = target.createChild(
22111                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
22112         }
22113         this.renderAll(ct, target);
22114     },
22115
22116     // private
22117     getRow : function(index){
22118         var row = this.table.tBodies[0].childNodes[index];
22119         if(!row){
22120             row = document.createElement('tr');
22121             this.table.tBodies[0].appendChild(row);
22122         }
22123         return row;
22124     },
22125
22126     // private
22127     getNextCell : function(c){
22128         var cell = this.getNextNonSpan(this.currentColumn, this.currentRow);
22129         var curCol = this.currentColumn = cell[0], curRow = this.currentRow = cell[1];
22130         for(var rowIndex = curRow; rowIndex < curRow + (c.rowspan || 1); rowIndex++){
22131             if(!this.cells[rowIndex]){
22132                 this.cells[rowIndex] = [];
22133             }
22134             for(var colIndex = curCol; colIndex < curCol + (c.colspan || 1); colIndex++){
22135                 this.cells[rowIndex][colIndex] = true;
22136             }
22137         }
22138         var td = document.createElement('td');
22139         if(c.cellId){
22140             td.id = c.cellId;
22141         }
22142         var cls = 'x-table-layout-cell';
22143         if(c.cellCls){
22144             cls += ' ' + c.cellCls;
22145         }
22146         td.className = cls;
22147         if(c.colspan){
22148             td.colSpan = c.colspan;
22149         }
22150         if(c.rowspan){
22151             td.rowSpan = c.rowspan;
22152         }
22153         this.getRow(curRow).appendChild(td);
22154         return td;
22155     },
22156
22157     // private
22158     getNextNonSpan: function(colIndex, rowIndex){
22159         var cols = this.columns;
22160         while((cols && colIndex >= cols) || (this.cells[rowIndex] && this.cells[rowIndex][colIndex])) {
22161             if(cols && colIndex >= cols){
22162                 rowIndex++;
22163                 colIndex = 0;
22164             }else{
22165                 colIndex++;
22166             }
22167         }
22168         return [colIndex, rowIndex];
22169     },
22170
22171     // private
22172     renderItem : function(c, position, target){
22173         // Ensure we have our inner table to get cells to render into.
22174         if(!this.table){
22175             this.table = target.createChild(
22176                 Ext.apply({tag:'table', cls:'x-table-layout', cellspacing: 0, cn: {tag: 'tbody'}}, this.tableAttrs), null, true);
22177         }
22178         if(c && !c.rendered){
22179             c.render(this.getNextCell(c));
22180             this.configureItem(c, position);
22181         }else if(c && !this.isValidParent(c, target)){
22182             var container = this.getNextCell(c);
22183             container.insertBefore(c.getPositionEl().dom, null);
22184             c.container = Ext.get(container);
22185             this.configureItem(c, position);
22186         }
22187     },
22188
22189     // private
22190     isValidParent : function(c, target){
22191         return c.getPositionEl().up('table', 5).dom.parentNode === (target.dom || target);
22192     }
22193
22194     /**
22195      * @property activeItem
22196      * @hide
22197      */
22198 });
22199
22200 Ext.Container.LAYOUTS['table'] = Ext.layout.TableLayout;/**
22201  * @class Ext.layout.AbsoluteLayout
22202  * @extends Ext.layout.AnchorLayout
22203  * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.AnchorLayout}</b> and adds the
22204  * ability for x/y positioning using the standard x and y component config options.</p>
22205  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
22206  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
22207  * <p>Example usage:</p>
22208  * <pre><code>
22209 var form = new Ext.form.FormPanel({
22210     title: 'Absolute Layout',
22211     layout:'absolute',
22212     layoutConfig: {
22213         // layout-specific configs go here
22214         extraCls: 'x-abs-layout-item',
22215     },
22216     baseCls: 'x-plain',
22217     url:'save-form.php',
22218     defaultType: 'textfield',
22219     items: [{
22220         x: 0,
22221         y: 5,
22222         xtype:'label',
22223         text: 'Send To:'
22224     },{
22225         x: 60,
22226         y: 0,
22227         name: 'to',
22228         anchor:'100%'  // anchor width by percentage
22229     },{
22230         x: 0,
22231         y: 35,
22232         xtype:'label',
22233         text: 'Subject:'
22234     },{
22235         x: 60,
22236         y: 30,
22237         name: 'subject',
22238         anchor: '100%'  // anchor width by percentage
22239     },{
22240         x:0,
22241         y: 60,
22242         xtype: 'textarea',
22243         name: 'msg',
22244         anchor: '100% 100%'  // anchor width and height
22245     }]
22246 });
22247 </code></pre>
22248  */
22249 Ext.layout.AbsoluteLayout = Ext.extend(Ext.layout.AnchorLayout, {
22250
22251     extraCls: 'x-abs-layout-item',
22252
22253     type: 'absolute',
22254
22255     onLayout : function(ct, target){
22256         target.position();
22257         this.paddingLeft = target.getPadding('l');
22258         this.paddingTop = target.getPadding('t');
22259         Ext.layout.AbsoluteLayout.superclass.onLayout.call(this, ct, target);
22260     },
22261
22262     // private
22263     adjustWidthAnchor : function(value, comp){
22264         return value ? value - comp.getPosition(true)[0] + this.paddingLeft : value;
22265     },
22266
22267     // private
22268     adjustHeightAnchor : function(value, comp){
22269         return  value ? value - comp.getPosition(true)[1] + this.paddingTop : value;
22270     }
22271     /**
22272      * @property activeItem
22273      * @hide
22274      */
22275 });
22276 Ext.Container.LAYOUTS['absolute'] = Ext.layout.AbsoluteLayout;
22277 /**
22278  * @class Ext.layout.BoxLayout
22279  * @extends Ext.layout.ContainerLayout
22280  * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
22281  */
22282 Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
22283     /**
22284      * @cfg {Object} defaultMargins
22285      * <p>If the individual contained items do not have a <tt>margins</tt>
22286      * property specified, the default margins from this property will be
22287      * applied to each item.</p>
22288      * <br><p>This property may be specified as an object containing margins
22289      * to apply in the format:</p><pre><code>
22290 {
22291     top: (top margin),
22292     right: (right margin),
22293     bottom: (bottom margin),
22294     left: (left margin)
22295 }</code></pre>
22296      * <p>This property may also be specified as a string containing
22297      * space-separated, numeric margin values. The order of the sides associated
22298      * with each value matches the way CSS processes margin values:</p>
22299      * <div class="mdetail-params"><ul>
22300      * <li>If there is only one value, it applies to all sides.</li>
22301      * <li>If there are two values, the top and bottom borders are set to the
22302      * first value and the right and left are set to the second.</li>
22303      * <li>If there are three values, the top is set to the first value, the left
22304      * and right are set to the second, and the bottom is set to the third.</li>
22305      * <li>If there are four values, they apply to the top, right, bottom, and
22306      * left, respectively.</li>
22307      * </ul></div>
22308      * <p>Defaults to:</p><pre><code>
22309      * {top:0, right:0, bottom:0, left:0}
22310      * </code></pre>
22311      */
22312     defaultMargins : {left:0,top:0,right:0,bottom:0},
22313     /**
22314      * @cfg {String} padding
22315      * <p>Sets the padding to be applied to all child items managed by this layout.</p>
22316      * <p>This property must be specified as a string containing
22317      * space-separated, numeric padding values. The order of the sides associated
22318      * with each value matches the way CSS processes padding values:</p>
22319      * <div class="mdetail-params"><ul>
22320      * <li>If there is only one value, it applies to all sides.</li>
22321      * <li>If there are two values, the top and bottom borders are set to the
22322      * first value and the right and left are set to the second.</li>
22323      * <li>If there are three values, the top is set to the first value, the left
22324      * and right are set to the second, and the bottom is set to the third.</li>
22325      * <li>If there are four values, they apply to the top, right, bottom, and
22326      * left, respectively.</li>
22327      * </ul></div>
22328      * <p>Defaults to: <code>"0"</code></p>
22329      */
22330     padding : '0',
22331     // documented in subclasses
22332     pack : 'start',
22333
22334     // private
22335     monitorResize : true,
22336     type: 'box',
22337     scrollOffset : 0,
22338     extraCls : 'x-box-item',
22339     targetCls : 'x-box-layout-ct',
22340     innerCls : 'x-box-inner',
22341
22342     constructor : function(config){
22343         Ext.layout.BoxLayout.superclass.constructor.call(this, config);
22344
22345         if (Ext.isString(this.defaultMargins)) {
22346             this.defaultMargins = this.parseMargins(this.defaultMargins);
22347         }
22348     },
22349
22350     /**
22351      * @private
22352      * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
22353      * when laying out
22354      */
22355     onLayout: function(container, target) {
22356         Ext.layout.BoxLayout.superclass.onLayout.call(this, container, target);
22357
22358         var items = this.getVisibleItems(container),
22359             tSize = this.getLayoutTargetSize();
22360
22361         /**
22362          * @private
22363          * @property layoutTargetLastSize
22364          * @type Object
22365          * Private cache of the last measured size of the layout target. This should never be used except by
22366          * BoxLayout subclasses during their onLayout run.
22367          */
22368         this.layoutTargetLastSize = tSize;
22369
22370         /**
22371          * @private
22372          * @property childBoxCache
22373          * @type Array
22374          * Array of the last calculated height, width, top and left positions of each visible rendered component
22375          * within the Box layout.
22376          */
22377         this.childBoxCache = this.calculateChildBoxes(items, tSize);
22378
22379         this.updateInnerCtSize(tSize, this.childBoxCache);
22380         this.updateChildBoxes(this.childBoxCache.boxes);
22381
22382         // Putting a box layout into an overflowed container is NOT correct and will make a second layout pass necessary.
22383         this.handleTargetOverflow(tSize, container, target);
22384     },
22385
22386     /**
22387      * Resizes and repositions each child component
22388      * @param {Array} boxes The box measurements
22389      */
22390     updateChildBoxes: function(boxes) {
22391         for (var i = 0, length = boxes.length; i < length; i++) {
22392             var box  = boxes[i],
22393                 comp = box.component;
22394
22395             if (box.dirtySize) {
22396                 comp.setSize(box.width, box.height);
22397             }
22398             // Don't set positions to NaN
22399             if (isNaN(box.left) || isNaN(box.top)) {
22400                 continue;
22401             }
22402             comp.setPosition(box.left, box.top);
22403         }
22404     },
22405
22406     /**
22407      * @private
22408      * Called by onRender just before the child components are sized and positioned. This resizes the innerCt
22409      * to make sure all child items fit within it. We call this before sizing the children because if our child
22410      * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them
22411      * again immediately afterwards, giving a performance hit.
22412      * Subclasses should provide an implementation.
22413      * @param {Object} currentSize The current height and width of the innerCt
22414      * @param {Array} calculations The new box calculations of all items to be laid out
22415      */
22416     updateInnerCtSize: Ext.emptyFn,
22417
22418     /**
22419      * @private
22420      * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden',
22421      * we need to lay out a second time because the scrollbars may have modified the height and width of the layout
22422      * target. Having a Box layout inside such a target is therefore not recommended.
22423      * @param {Object} previousTargetSize The size and height of the layout target before we just laid out
22424      * @param {Ext.Container} container The container
22425      * @param {Ext.Element} target The target element
22426      */
22427     handleTargetOverflow: function(previousTargetSize, container, target) {
22428         var overflow = target.getStyle('overflow');
22429
22430         if (overflow && overflow != 'hidden' &&!this.adjustmentPass) {
22431             var newTargetSize = this.getLayoutTargetSize();
22432             if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height){
22433                 this.adjustmentPass = true;
22434                 this.onLayout(container, target);
22435             }
22436         }
22437
22438         delete this.adjustmentPass;
22439     },
22440
22441     // private
22442     isValidParent : function(c, target){
22443         return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom;
22444     },
22445
22446     /**
22447      * @private
22448      * Returns all items that are both rendered and visible
22449      * @return {Array} All matching items
22450      */
22451     getVisibleItems: function(ct) {
22452         var ct  = ct || this.container,
22453             t   = ct.getLayoutTarget(),
22454             cti = ct.items.items,
22455             len = cti.length,
22456
22457             i, c, items = [];
22458
22459         for (i = 0; i < len; i++) {
22460             if((c = cti[i]).rendered && this.isValidParent(c, t) && c.hidden !== true  && c.collapsed !== true){
22461                 items.push(c);
22462             }
22463         }
22464
22465         return items;
22466     },
22467
22468     // private
22469     renderAll : function(ct, target){
22470         if(!this.innerCt){
22471             // the innerCt prevents wrapping and shuffling while
22472             // the container is resizing
22473             this.innerCt = target.createChild({cls:this.innerCls});
22474             this.padding = this.parseMargins(this.padding);
22475         }
22476         Ext.layout.BoxLayout.superclass.renderAll.call(this, ct, this.innerCt);
22477     },
22478
22479     getLayoutTargetSize : function(){
22480         var target = this.container.getLayoutTarget(), ret;
22481         if (target) {
22482             ret = target.getViewSize();
22483
22484             // IE in strict mode will return a width of 0 on the 1st pass of getViewSize.
22485             // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
22486             // with getViewSize
22487             if (Ext.isIE && Ext.isStrict && ret.width == 0){
22488                 ret =  target.getStyleSize();
22489             }
22490
22491             ret.width -= target.getPadding('lr');
22492             ret.height -= target.getPadding('tb');
22493         }
22494         return ret;
22495     },
22496
22497     // private
22498     renderItem : function(c){
22499         if(Ext.isString(c.margins)){
22500             c.margins = this.parseMargins(c.margins);
22501         }else if(!c.margins){
22502             c.margins = this.defaultMargins;
22503         }
22504         Ext.layout.BoxLayout.superclass.renderItem.apply(this, arguments);
22505     }
22506 });
22507
22508 /**
22509  * @class Ext.layout.VBoxLayout
22510  * @extends Ext.layout.BoxLayout
22511  * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
22512  * space between child items containing a numeric <code>flex</code> configuration.</p>
22513  * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
22514  */
22515 Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
22516     /**
22517      * @cfg {String} align
22518      * Controls how the child items of the container are aligned. Acceptable configuration values for this
22519      * property are:
22520      * <div class="mdetail-params"><ul>
22521      * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
22522      * at the <b>left</b> side of the container</div></li>
22523      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
22524      * <b>mid-width</b> of the container</div></li>
22525      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
22526      * the width of the container</div></li>
22527      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
22528      * the size of the largest item.</div></li>
22529      * </ul></div>
22530      */
22531     align : 'left', // left, center, stretch, strechmax
22532     type: 'vbox',
22533
22534     /**
22535      * @cfg {String} pack
22536      * Controls how the child items of the container are packed together. Acceptable configuration values
22537      * for this property are:
22538      * <div class="mdetail-params"><ul>
22539      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
22540      * <b>top</b> side of container</div></li>
22541      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
22542      * <b>mid-height</b> of container</div></li>
22543      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>bottom</b>
22544      * side of container</div></li>
22545      * </ul></div>
22546      */
22547
22548     /**
22549      * @cfg {Number} flex
22550      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
22551      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>vertically</b>
22552      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
22553      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
22554      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
22555      */
22556
22557     /**
22558      * @private
22559      * See parent documentation
22560      */
22561     updateInnerCtSize: function(tSize, calcs) {
22562         var innerCtHeight = tSize.height,
22563             innerCtWidth  = calcs.meta.maxWidth + this.padding.left + this.padding.right;
22564
22565         if (this.align == 'stretch') {
22566             innerCtWidth = tSize.width;
22567         } else if (this.align == 'center') {
22568             innerCtWidth = Math.max(tSize.width, innerCtWidth);
22569         }
22570
22571         //we set the innerCt size first because if our child items are larger than the previous innerCt size
22572         //the browser will insert scrollbars and then remove them again immediately afterwards
22573         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
22574     },
22575
22576     /**
22577      * @private
22578      * Calculates the size and positioning of each item in the VBox. This iterates over all of the rendered,
22579      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
22580      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
22581      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
22582      * @param {Object} targetSize Object containing target size and height
22583      * @return {Object} Object containing box measurements for each child, plus meta data
22584      */
22585     calculateChildBoxes: function(visibleItems, targetSize) {
22586         var visibleCount = visibleItems.length,
22587
22588             padding      = this.padding,
22589             topOffset    = padding.top,
22590             leftOffset   = padding.left,
22591             paddingVert  = topOffset  + padding.bottom,
22592             paddingHoriz = leftOffset + padding.right,
22593
22594             width        = targetSize.width - this.scrollOffset,
22595             height       = targetSize.height,
22596             availWidth   = Math.max(0, width - paddingHoriz),
22597
22598             isStart      = this.pack == 'start',
22599             isCenter     = this.pack == 'center',
22600             isEnd        = this.pack == 'end',
22601
22602             nonFlexHeight= 0,
22603             maxWidth     = 0,
22604             totalFlex    = 0,
22605
22606             //used to cache the calculated size and position values for each child item
22607             boxes        = [],
22608
22609             //used in the for loops below, just declared here for brevity
22610             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedHeight, horizMargins, stretchWidth;
22611
22612             //gather the total flex of all flexed items and the width taken up by fixed width items
22613             for (i = 0; i < visibleCount; i++) {
22614                 child = visibleItems[i];
22615                 childHeight = child.height;
22616                 childWidth  = child.width;
22617                 canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
22618
22619
22620                 // Static height (numeric) requires no calcs
22621                 if (!Ext.isNumber(childHeight)) {
22622
22623                     // flex and not 'auto' height
22624                     if (child.flex && !childHeight) {
22625                         totalFlex += child.flex;
22626
22627                     // Not flexed or 'auto' height or undefined height
22628                     } else {
22629                         //Render and layout sub-containers without a flex or width defined, as otherwise we
22630                         //don't know how wide the sub-container should be and cannot calculate flexed widths
22631                         if (!childHeight && canLayout) {
22632                             child.doLayout();
22633                         }
22634
22635                         childSize = child.getSize();
22636                         childWidth = childSize.width;
22637                         childHeight = childSize.height;
22638                     }
22639                 }
22640
22641                 childMargins = child.margins;
22642
22643                 nonFlexHeight += (childHeight || 0) + childMargins.top + childMargins.bottom;
22644
22645                 // Max width for align - force layout of non-layed out subcontainers without a numeric width
22646                 if (!Ext.isNumber(childWidth)) {
22647                     if (canLayout) {
22648                         child.doLayout();
22649                     }
22650                     childWidth = child.getWidth();
22651                 }
22652
22653                 maxWidth = Math.max(maxWidth, childWidth + childMargins.left + childMargins.right);
22654
22655                 //cache the size of each child component
22656                 boxes.push({
22657                     component: child,
22658                     height   : childHeight || undefined,
22659                     width    : childWidth || undefined
22660                 });
22661             }
22662
22663             //the height available to the flexed items
22664             var availableHeight = Math.max(0, (height - nonFlexHeight - paddingVert));
22665
22666             if (isCenter) {
22667                 topOffset += availableHeight / 2;
22668             } else if (isEnd) {
22669                 topOffset += availableHeight;
22670             }
22671
22672             //temporary variables used in the flex height calculations below
22673             var remainingHeight = availableHeight,
22674                 remainingFlex   = totalFlex;
22675
22676             //calculate the height of each flexed item, and the left + top positions of every item
22677             for (i = 0; i < visibleCount; i++) {
22678                 child = visibleItems[i];
22679                 calcs = boxes[i];
22680
22681                 childMargins = child.margins;
22682                 horizMargins = childMargins.left + childMargins.right;
22683
22684                 topOffset   += childMargins.top;
22685
22686                 if (isStart && child.flex && !child.height) {
22687                     flexedHeight     = Math.ceil((child.flex / remainingFlex) * remainingHeight);
22688                     remainingHeight -= flexedHeight;
22689                     remainingFlex   -= child.flex;
22690
22691                     calcs.height = flexedHeight;
22692                     calcs.dirtySize = true;
22693                 }
22694
22695                 calcs.left = leftOffset + childMargins.left;
22696                 calcs.top  = topOffset;
22697
22698                 switch (this.align) {
22699                     case 'stretch':
22700                         stretchWidth = availWidth - horizMargins;
22701                         calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
22702                         calcs.dirtySize = true;
22703                         break;
22704                     case 'stretchmax':
22705                         stretchWidth = maxWidth - horizMargins;
22706                         calcs.width  = stretchWidth.constrain(child.minWidth || 0, child.maxWidth || 1000000);
22707                         calcs.dirtySize = true;
22708                         break;
22709                     case 'center':
22710                         var diff = availWidth - calcs.width - horizMargins;
22711                         if (diff > 0) {
22712                             calcs.left = leftOffset + horizMargins + (diff / 2);
22713                         }
22714                 }
22715
22716                 topOffset += calcs.height + childMargins.bottom;
22717             }
22718
22719         return {
22720             boxes: boxes,
22721             meta : {
22722                 maxWidth: maxWidth
22723             }
22724         };
22725     }
22726 });
22727
22728 Ext.Container.LAYOUTS.vbox = Ext.layout.VBoxLayout;
22729
22730 /**
22731  * @class Ext.layout.HBoxLayout
22732  * @extends Ext.layout.BoxLayout
22733  * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
22734  * space between child items containing a numeric <code>flex</code> configuration.</p>
22735  * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
22736  */
22737 Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
22738     /**
22739      * @cfg {String} align
22740      * Controls how the child items of the container are aligned. Acceptable configuration values for this
22741      * property are:
22742      * <div class="mdetail-params"><ul>
22743      * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
22744      * at the <b>top</b> of the container</div></li>
22745      * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
22746      * <b>middle</b> of the container</div></li>
22747      * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
22748      * the height of the container</div></li>
22749      * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
22750      * the height of the largest item.</div></li>
22751      */
22752     align: 'top', // top, middle, stretch, strechmax
22753
22754     type : 'hbox',
22755
22756     /**
22757      * @private
22758      * See parent documentation
22759      */
22760     updateInnerCtSize: function(tSize, calcs) {
22761         var innerCtWidth  = tSize.width,
22762             innerCtHeight = calcs.meta.maxHeight + this.padding.top + this.padding.bottom;
22763
22764         if (this.align == 'stretch') {
22765             innerCtHeight = tSize.height;
22766         } else if (this.align == 'middle') {
22767             innerCtHeight = Math.max(tSize.height, innerCtHeight);
22768         }
22769
22770         this.innerCt.setSize(innerCtWidth || undefined, innerCtHeight || undefined);
22771     },
22772
22773     /**
22774      * @cfg {String} pack
22775      * Controls how the child items of the container are packed together. Acceptable configuration values
22776      * for this property are:
22777      * <div class="mdetail-params"><ul>
22778      * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
22779      * <b>left</b> side of container</div></li>
22780      * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
22781      * <b>mid-width</b> of container</div></li>
22782      * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
22783      * side of container</div></li>
22784      * </ul></div>
22785      */
22786     /**
22787      * @cfg {Number} flex
22788      * This configuation option is to be applied to <b>child <tt>items</tt></b> of the container managed
22789      * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
22790      * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
22791      * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
22792      * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
22793      */
22794
22795     /**
22796      * @private
22797      * Calculates the size and positioning of each item in the HBox. This iterates over all of the rendered,
22798      * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
22799      * returns meta data such as maxHeight which are useful when resizing layout wrappers such as this.innerCt.
22800      * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
22801      * @param {Object} targetSize Object containing target size and height
22802      * @return {Object} Object containing box measurements for each child, plus meta data
22803      */
22804     calculateChildBoxes: function(visibleItems, targetSize) {
22805         var visibleCount = visibleItems.length,
22806
22807             padding      = this.padding,
22808             topOffset    = padding.top,
22809             leftOffset   = padding.left,
22810             paddingVert  = topOffset  + padding.bottom,
22811             paddingHoriz = leftOffset + padding.right,
22812
22813             width        = targetSize.width - this.scrollOffset,
22814             height       = targetSize.height,
22815             availHeight  = Math.max(0, height - paddingVert),
22816
22817             isStart      = this.pack == 'start',
22818             isCenter     = this.pack == 'center',
22819             isEnd        = this.pack == 'end',
22820             // isRestore    = ['stretch', 'stretchmax'].indexOf(this.align) == -1,
22821
22822             nonFlexWidth = 0,
22823             maxHeight    = 0,
22824             totalFlex    = 0,
22825
22826             //used to cache the calculated size and position values for each child item
22827             boxes        = [],
22828
22829             //used in the for loops below, just declared here for brevity
22830             child, childWidth, childHeight, childSize, childMargins, canLayout, i, calcs, flexedWidth, vertMargins, stretchHeight;
22831
22832             //gather the total flex of all flexed items and the width taken up by fixed width items
22833             for (i = 0; i < visibleCount; i++) {
22834                 child       = visibleItems[i];
22835                 childHeight = child.height;
22836                 childWidth  = child.width;
22837                 canLayout   = !child.hasLayout && Ext.isFunction(child.doLayout);
22838
22839                 // Static width (numeric) requires no calcs
22840                 if (!Ext.isNumber(childWidth)) {
22841
22842                     // flex and not 'auto' width
22843                     if (child.flex && !childWidth) {
22844                         totalFlex += child.flex;
22845
22846                     // Not flexed or 'auto' width or undefined width
22847                     } else {
22848                         //Render and layout sub-containers without a flex or width defined, as otherwise we
22849                         //don't know how wide the sub-container should be and cannot calculate flexed widths
22850                         if (!childWidth && canLayout) {
22851                             child.doLayout();
22852                         }
22853
22854                         childSize   = child.getSize();
22855                         childWidth  = childSize.width;
22856                         childHeight = childSize.height;
22857                     }
22858                 }
22859
22860                 childMargins = child.margins;
22861
22862                 nonFlexWidth += (childWidth || 0) + childMargins.left + childMargins.right;
22863
22864                 // Max height for align - force layout of non-layed out subcontainers without a numeric height
22865                 if (!Ext.isNumber(childHeight)) {
22866                     if (canLayout) {
22867                         child.doLayout();
22868                     }
22869                     childHeight = child.getHeight();
22870                 }
22871
22872                 maxHeight = Math.max(maxHeight, childHeight + childMargins.top + childMargins.bottom);
22873
22874                 //cache the size of each child component
22875                 boxes.push({
22876                     component: child,
22877                     height   : childHeight || undefined,
22878                     width    : childWidth || undefined
22879                 });
22880             }
22881
22882             //the width available to the flexed items
22883             var availableWidth = Math.max(0, (width - nonFlexWidth - paddingHoriz));
22884
22885             if (isCenter) {
22886                 leftOffset += availableWidth / 2;
22887             } else if (isEnd) {
22888                 leftOffset += availableWidth;
22889             }
22890
22891             //temporary variables used in the flex width calculations below
22892             var remainingWidth = availableWidth,
22893                 remainingFlex  = totalFlex;
22894
22895             //calculate the widths of each flexed item, and the left + top positions of every item
22896             for (i = 0; i < visibleCount; i++) {
22897                 child = visibleItems[i];
22898                 calcs = boxes[i];
22899
22900                 childMargins = child.margins;
22901                 vertMargins  = childMargins.top + childMargins.bottom;
22902
22903                 leftOffset  += childMargins.left;
22904
22905                 if (isStart && child.flex && !child.width) {
22906                     flexedWidth     = Math.ceil((child.flex / remainingFlex) * remainingWidth);
22907                     remainingWidth -= flexedWidth;
22908                     remainingFlex  -= child.flex;
22909
22910                     calcs.width = flexedWidth;
22911                     calcs.dirtySize = true;
22912                 }
22913
22914                 calcs.left = leftOffset;
22915                 calcs.top  = topOffset + childMargins.top;
22916
22917                 switch (this.align) {
22918                     case 'stretch':
22919                         stretchHeight = availHeight - vertMargins;
22920                         calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
22921                         calcs.dirtySize = true;
22922                         break;
22923                     case 'stretchmax':
22924                         stretchHeight = maxHeight - vertMargins;
22925                         calcs.height  = stretchHeight.constrain(child.minHeight || 0, child.maxHeight || 1000000);
22926                         calcs.dirtySize = true;
22927                         break;
22928                     case 'middle':
22929                         var diff = availHeight - calcs.height - vertMargins;
22930                         if (diff > 0) {
22931                             calcs.top = topOffset + vertMargins + (diff / 2);
22932                         }
22933                 }
22934                 leftOffset += calcs.width + childMargins.right;
22935             }
22936
22937         return {
22938             boxes: boxes,
22939             meta : {
22940                 maxHeight: maxHeight
22941             }
22942         };
22943     }
22944 });
22945
22946 Ext.Container.LAYOUTS.hbox = Ext.layout.HBoxLayout;
22947 /**
22948  * @class Ext.layout.ToolbarLayout
22949  * @extends Ext.layout.ContainerLayout
22950  * Layout manager used by Ext.Toolbar. This is highly specialised for use by Toolbars and would not
22951  * usually be used by any other class.
22952  */
22953 Ext.layout.ToolbarLayout = Ext.extend(Ext.layout.ContainerLayout, {
22954     monitorResize : true,
22955
22956     type: 'toolbar',
22957
22958     /**
22959      * @property triggerWidth
22960      * @type Number
22961      * The width allocated for the menu trigger at the extreme right end of the Toolbar
22962      */
22963     triggerWidth: 18,
22964
22965     /**
22966      * @property noItemsMenuText
22967      * @type String
22968      * HTML fragment to render into the toolbar overflow menu if there are no items to display
22969      */
22970     noItemsMenuText : '<div class="x-toolbar-no-items">(None)</div>',
22971
22972     /**
22973      * @private
22974      * @property lastOverflow
22975      * @type Boolean
22976      * Used internally to record whether the last layout caused an overflow or not
22977      */
22978     lastOverflow: false,
22979
22980     /**
22981      * @private
22982      * @property tableHTML
22983      * @type String
22984      * String used to build the HTML injected to support the Toolbar's layout. The align property is
22985      * injected into this string inside the td.x-toolbar-left element during onLayout.
22986      */
22987     tableHTML: [
22988         '<table cellspacing="0" class="x-toolbar-ct">',
22989             '<tbody>',
22990                 '<tr>',
22991                     '<td class="x-toolbar-left" align="{0}">',
22992                         '<table cellspacing="0">',
22993                             '<tbody>',
22994                                 '<tr class="x-toolbar-left-row"></tr>',
22995                             '</tbody>',
22996                         '</table>',
22997                     '</td>',
22998                     '<td class="x-toolbar-right" align="right">',
22999                         '<table cellspacing="0" class="x-toolbar-right-ct">',
23000                             '<tbody>',
23001                                 '<tr>',
23002                                     '<td>',
23003                                         '<table cellspacing="0">',
23004                                             '<tbody>',
23005                                                 '<tr class="x-toolbar-right-row"></tr>',
23006                                             '</tbody>',
23007                                         '</table>',
23008                                     '</td>',
23009                                     '<td>',
23010                                         '<table cellspacing="0">',
23011                                             '<tbody>',
23012                                                 '<tr class="x-toolbar-extras-row"></tr>',
23013                                             '</tbody>',
23014                                         '</table>',
23015                                     '</td>',
23016                                 '</tr>',
23017                             '</tbody>',
23018                         '</table>',
23019                     '</td>',
23020                 '</tr>',
23021             '</tbody>',
23022         '</table>'
23023     ].join(""),
23024
23025     /**
23026      * @private
23027      * Create the wrapping Toolbar HTML and render/move all the items into the correct places
23028      */
23029     onLayout : function(ct, target) {
23030         //render the Toolbar <table> HTML if it's not already present
23031         if (!this.leftTr) {
23032             var align = ct.buttonAlign == 'center' ? 'center' : 'left';
23033
23034             target.addClass('x-toolbar-layout-ct');
23035             target.insertHtml('beforeEnd', String.format(this.tableHTML, align));
23036
23037             this.leftTr   = target.child('tr.x-toolbar-left-row', true);
23038             this.rightTr  = target.child('tr.x-toolbar-right-row', true);
23039             this.extrasTr = target.child('tr.x-toolbar-extras-row', true);
23040
23041             if (this.hiddenItem == undefined) {
23042                 /**
23043                  * @property hiddenItems
23044                  * @type Array
23045                  * Holds all items that are currently hidden due to there not being enough space to render them
23046                  * These items will appear on the expand menu.
23047                  */
23048                 this.hiddenItems = [];
23049             }
23050         }
23051
23052         var side     = ct.buttonAlign == 'right' ? this.rightTr : this.leftTr,
23053             items    = ct.items.items,
23054             position = 0;
23055
23056         //render each item if not already rendered, place it into the correct (left or right) target
23057         for (var i = 0, len = items.length, c; i < len; i++, position++) {
23058             c = items[i];
23059
23060             if (c.isFill) {
23061                 side   = this.rightTr;
23062                 position = -1;
23063             } else if (!c.rendered) {
23064                 c.render(this.insertCell(c, side, position));
23065             } else {
23066                 if (!c.xtbHidden && !this.isValidParent(c, side.childNodes[position])) {
23067                     var td = this.insertCell(c, side, position);
23068                     td.appendChild(c.getPositionEl().dom);
23069                     c.container = Ext.get(td);
23070                 }
23071             }
23072         }
23073
23074         //strip extra empty cells
23075         this.cleanup(this.leftTr);
23076         this.cleanup(this.rightTr);
23077         this.cleanup(this.extrasTr);
23078         this.fitToSize(target);
23079     },
23080
23081     /**
23082      * @private
23083      * Removes any empty nodes from the given element
23084      * @param {Ext.Element} el The element to clean up
23085      */
23086     cleanup : function(el) {
23087         var cn = el.childNodes, i, c;
23088
23089         for (i = cn.length-1; i >= 0 && (c = cn[i]); i--) {
23090             if (!c.firstChild) {
23091                 el.removeChild(c);
23092             }
23093         }
23094     },
23095
23096     /**
23097      * @private
23098      * Inserts the given Toolbar item into the given element
23099      * @param {Ext.Component} c The component to add
23100      * @param {Ext.Element} target The target to add the component to
23101      * @param {Number} position The position to add the component at
23102      */
23103     insertCell : function(c, target, position) {
23104         var td = document.createElement('td');
23105         td.className = 'x-toolbar-cell';
23106
23107         target.insertBefore(td, target.childNodes[position] || null);
23108
23109         return td;
23110     },
23111
23112     /**
23113      * @private
23114      * Hides an item because it will not fit in the available width. The item will be unhidden again
23115      * if the Toolbar is resized to be large enough to show it
23116      * @param {Ext.Component} item The item to hide
23117      */
23118     hideItem : function(item) {
23119         this.hiddenItems.push(item);
23120
23121         item.xtbHidden = true;
23122         item.xtbWidth = item.getPositionEl().dom.parentNode.offsetWidth;
23123         item.hide();
23124     },
23125
23126     /**
23127      * @private
23128      * Unhides an item that was previously hidden due to there not being enough space left on the Toolbar
23129      * @param {Ext.Component} item The item to show
23130      */
23131     unhideItem : function(item) {
23132         item.show();
23133         item.xtbHidden = false;
23134         this.hiddenItems.remove(item);
23135     },
23136
23137     /**
23138      * @private
23139      * Returns the width of the given toolbar item. If the item is currently hidden because there
23140      * is not enough room to render it, its previous width is returned
23141      * @param {Ext.Component} c The component to measure
23142      * @return {Number} The width of the item
23143      */
23144     getItemWidth : function(c) {
23145         return c.hidden ? (c.xtbWidth || 0) : c.getPositionEl().dom.parentNode.offsetWidth;
23146     },
23147
23148     /**
23149      * @private
23150      * Called at the end of onLayout. At this point the Toolbar has already been resized, so we need
23151      * to fit the items into the available width. We add up the width required by all of the items in
23152      * the toolbar - if we don't have enough space we hide the extra items and render the expand menu
23153      * trigger.
23154      * @param {Ext.Element} target The Element the Toolbar is currently laid out within
23155      */
23156     fitToSize : function(target) {
23157         if (this.container.enableOverflow === false) {
23158             return;
23159         }
23160
23161         var width       = target.dom.clientWidth,
23162             tableWidth  = target.dom.firstChild.offsetWidth,
23163             clipWidth   = width - this.triggerWidth,
23164             lastWidth   = this.lastWidth || 0,
23165
23166             hiddenItems = this.hiddenItems,
23167             hasHiddens  = hiddenItems.length != 0,
23168             isLarger    = width >= lastWidth;
23169
23170         this.lastWidth  = width;
23171
23172         if (tableWidth > width || (hasHiddens && isLarger)) {
23173             var items     = this.container.items.items,
23174                 len       = items.length,
23175                 loopWidth = 0,
23176                 item;
23177
23178             for (var i = 0; i < len; i++) {
23179                 item = items[i];
23180
23181                 if (!item.isFill) {
23182                     loopWidth += this.getItemWidth(item);
23183                     if (loopWidth > clipWidth) {
23184                         if (!(item.hidden || item.xtbHidden)) {
23185                             this.hideItem(item);
23186                         }
23187                     } else if (item.xtbHidden) {
23188                         this.unhideItem(item);
23189                     }
23190                 }
23191             }
23192         }
23193
23194         //test for number of hidden items again here because they may have changed above
23195         hasHiddens = hiddenItems.length != 0;
23196
23197         if (hasHiddens) {
23198             this.initMore();
23199
23200             if (!this.lastOverflow) {
23201                 this.container.fireEvent('overflowchange', this.container, true);
23202                 this.lastOverflow = true;
23203             }
23204         } else if (this.more) {
23205             this.clearMenu();
23206             this.more.destroy();
23207             delete this.more;
23208
23209             if (this.lastOverflow) {
23210                 this.container.fireEvent('overflowchange', this.container, false);
23211                 this.lastOverflow = false;
23212             }
23213         }
23214     },
23215
23216     /**
23217      * @private
23218      * Returns a menu config for a given component. This config is used to create a menu item
23219      * to be added to the expander menu
23220      * @param {Ext.Component} component The component to create the config for
23221      * @param {Boolean} hideOnClick Passed through to the menu item
23222      */
23223     createMenuConfig : function(component, hideOnClick){
23224         var config = Ext.apply({}, component.initialConfig),
23225             group  = component.toggleGroup;
23226
23227         Ext.copyTo(config, component, [
23228             'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
23229         ]);
23230
23231         Ext.apply(config, {
23232             text       : component.overflowText || component.text,
23233             hideOnClick: hideOnClick
23234         });
23235
23236         if (group || component.enableToggle) {
23237             Ext.apply(config, {
23238                 group  : group,
23239                 checked: component.pressed,
23240                 listeners: {
23241                     checkchange: function(item, checked){
23242                         component.toggle(checked);
23243                     }
23244                 }
23245             });
23246         }
23247
23248         delete config.ownerCt;
23249         delete config.xtype;
23250         delete config.id;
23251
23252         return config;
23253     },
23254
23255     /**
23256      * @private
23257      * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
23258      * @param {Ext.menu.Menu} menu The menu to add to
23259      * @param {Ext.Component} component The component to add
23260      */
23261     addComponentToMenu : function(menu, component) {
23262         if (component instanceof Ext.Toolbar.Separator) {
23263             menu.add('-');
23264
23265         } else if (Ext.isFunction(component.isXType)) {
23266             if (component.isXType('splitbutton')) {
23267                 menu.add(this.createMenuConfig(component, true));
23268
23269             } else if (component.isXType('button')) {
23270                 menu.add(this.createMenuConfig(component, !component.menu));
23271
23272             } else if (component.isXType('buttongroup')) {
23273                 component.items.each(function(item){
23274                      this.addComponentToMenu(menu, item);
23275                 }, this);
23276             }
23277         }
23278     },
23279
23280     /**
23281      * @private
23282      * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
23283      * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
23284      */
23285     clearMenu : function(){
23286         var menu = this.moreMenu;
23287         if (menu && menu.items) {
23288             menu.items.each(function(item){
23289                 delete item.menu;
23290             });
23291         }
23292     },
23293
23294     /**
23295      * @private
23296      * Called before the expand menu is shown, this rebuilds the menu since it was last shown because
23297      * it is possible that the items hidden due to space limitations on the Toolbar have changed since.
23298      * @param {Ext.menu.Menu} m The menu
23299      */
23300     beforeMoreShow : function(menu) {
23301         var items = this.container.items.items,
23302             len   = items.length,
23303             item,
23304             prev;
23305
23306         var needsSep = function(group, item){
23307             return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
23308         };
23309
23310         this.clearMenu();
23311         menu.removeAll();
23312         for (var i = 0; i < len; i++) {
23313             item = items[i];
23314             if (item.xtbHidden) {
23315                 if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
23316                     menu.add('-');
23317                 }
23318                 this.addComponentToMenu(menu, item);
23319                 prev = item;
23320             }
23321         }
23322
23323         // put something so the menu isn't empty if no compatible items found
23324         if (menu.items.length < 1) {
23325             menu.add(this.noItemsMenuText);
23326         }
23327     },
23328
23329     /**
23330      * @private
23331      * Creates the expand trigger and menu, adding them to the <tr> at the extreme right of the
23332      * Toolbar table
23333      */
23334     initMore : function(){
23335         if (!this.more) {
23336             /**
23337              * @private
23338              * @property moreMenu
23339              * @type Ext.menu.Menu
23340              * The expand menu - holds items for every Toolbar item that cannot be shown
23341              * because the Toolbar is currently not wide enough.
23342              */
23343             this.moreMenu = new Ext.menu.Menu({
23344                 ownerCt : this.container,
23345                 listeners: {
23346                     beforeshow: this.beforeMoreShow,
23347                     scope: this
23348                 }
23349             });
23350
23351             /**
23352              * @private
23353              * @property more
23354              * @type Ext.Button
23355              * The expand button which triggers the overflow menu to be shown
23356              */
23357             this.more = new Ext.Button({
23358                 iconCls: 'x-toolbar-more-icon',
23359                 cls    : 'x-toolbar-more',
23360                 menu   : this.moreMenu,
23361                 ownerCt: this.container
23362             });
23363
23364             var td = this.insertCell(this.more, this.extrasTr, 100);
23365             this.more.render(td);
23366         }
23367     },
23368
23369     destroy : function(){
23370         Ext.destroy(this.more, this.moreMenu);
23371         delete this.leftTr;
23372         delete this.rightTr;
23373         delete this.extrasTr;
23374         Ext.layout.ToolbarLayout.superclass.destroy.call(this);
23375     }
23376 });
23377
23378 Ext.Container.LAYOUTS.toolbar = Ext.layout.ToolbarLayout;
23379 /**
23380  * @class Ext.layout.MenuLayout
23381  * @extends Ext.layout.ContainerLayout
23382  * <p>Layout manager used by {@link Ext.menu.Menu}. Generally this class should not need to be used directly.</p>
23383  */
23384  Ext.layout.MenuLayout = Ext.extend(Ext.layout.ContainerLayout, {
23385     monitorResize : true,
23386
23387     type: 'menu',
23388
23389     setContainer : function(ct){
23390         this.monitorResize = !ct.floating;
23391         // This event is only fired by the menu in IE, used so we don't couple
23392         // the menu with the layout.
23393         ct.on('autosize', this.doAutoSize, this);
23394         Ext.layout.MenuLayout.superclass.setContainer.call(this, ct);
23395     },
23396
23397     renderItem : function(c, position, target){
23398         if (!this.itemTpl) {
23399             this.itemTpl = Ext.layout.MenuLayout.prototype.itemTpl = new Ext.XTemplate(
23400                 '<li id="{itemId}" class="{itemCls}">',
23401                     '<tpl if="needsIcon">',
23402                         '<img src="{icon}" class="{iconCls}"/>',
23403                     '</tpl>',
23404                 '</li>'
23405             );
23406         }
23407
23408         if(c && !c.rendered){
23409             if(Ext.isNumber(position)){
23410                 position = target.dom.childNodes[position];
23411             }
23412             var a = this.getItemArgs(c);
23413
23414 //          The Component's positionEl is the <li> it is rendered into
23415             c.render(c.positionEl = position ?
23416                 this.itemTpl.insertBefore(position, a, true) :
23417                 this.itemTpl.append(target, a, true));
23418
23419 //          Link the containing <li> to the item.
23420             c.positionEl.menuItemId = c.getItemId();
23421
23422 //          If rendering a regular Component, and it needs an icon,
23423 //          move the Component rightwards.
23424             if (!a.isMenuItem && a.needsIcon) {
23425                 c.positionEl.addClass('x-menu-list-item-indent');
23426             }
23427             this.configureItem(c, position);
23428         }else if(c && !this.isValidParent(c, target)){
23429             if(Ext.isNumber(position)){
23430                 position = target.dom.childNodes[position];
23431             }
23432             target.dom.insertBefore(c.getActionEl().dom, position || null);
23433         }
23434     },
23435
23436     getItemArgs : function(c) {
23437         var isMenuItem = c instanceof Ext.menu.Item;
23438         return {
23439             isMenuItem: isMenuItem,
23440             needsIcon: !isMenuItem && (c.icon || c.iconCls),
23441             icon: c.icon || Ext.BLANK_IMAGE_URL,
23442             iconCls: 'x-menu-item-icon ' + (c.iconCls || ''),
23443             itemId: 'x-menu-el-' + c.id,
23444             itemCls: 'x-menu-list-item '
23445         };
23446     },
23447
23448     //  Valid if the Component is in a <li> which is part of our target <ul>
23449     isValidParent : function(c, target) {
23450         return c.el.up('li.x-menu-list-item', 5).dom.parentNode === (target.dom || target);
23451     },
23452
23453     onLayout : function(ct, target){
23454         Ext.layout.MenuLayout.superclass.onLayout.call(this, ct, target);
23455         this.doAutoSize();
23456     },
23457
23458     doAutoSize : function(){
23459         var ct = this.container, w = ct.width;
23460         if(ct.floating){
23461             if(w){
23462                 ct.setWidth(w);
23463             }else if(Ext.isIE){
23464                 ct.setWidth(Ext.isStrict && (Ext.isIE7 || Ext.isIE8) ? 'auto' : ct.minWidth);
23465                 var el = ct.getEl(), t = el.dom.offsetWidth; // force recalc
23466                 ct.setWidth(ct.getLayoutTarget().getWidth() + el.getFrameWidth('lr'));
23467             }
23468         }
23469     }
23470 });
23471 Ext.Container.LAYOUTS['menu'] = Ext.layout.MenuLayout;
23472 /**
23473  * @class Ext.Viewport
23474  * @extends Ext.Container
23475  * <p>A specialized container representing the viewable application area (the browser viewport).</p>
23476  * <p>The Viewport renders itself to the document body, and automatically sizes itself to the size of
23477  * the browser viewport and manages window resizing. There may only be one Viewport created
23478  * in a page. Inner layouts are available by virtue of the fact that all {@link Ext.Panel Panel}s
23479  * added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}
23480  * method of any of its child Panels may themselves have a layout.</p>
23481  * <p>The Viewport does not provide scrolling, so child Panels within the Viewport should provide
23482  * for scrolling if needed using the {@link #autoScroll} config.</p>
23483  * <p>An example showing a classic application border layout:</p><pre><code>
23484 new Ext.Viewport({
23485     layout: 'border',
23486     items: [{
23487         region: 'north',
23488         html: '&lt;h1 class="x-panel-header">Page Title&lt;/h1>',
23489         autoHeight: true,
23490         border: false,
23491         margins: '0 0 5 0'
23492     }, {
23493         region: 'west',
23494         collapsible: true,
23495         title: 'Navigation',
23496         width: 200
23497         // the west region might typically utilize a {@link Ext.tree.TreePanel TreePanel} or a Panel with {@link Ext.layout.AccordionLayout Accordion layout}
23498     }, {
23499         region: 'south',
23500         title: 'Title for Panel',
23501         collapsible: true,
23502         html: 'Information goes here',
23503         split: true,
23504         height: 100,
23505         minHeight: 100
23506     }, {
23507         region: 'east',
23508         title: 'Title for the Grid Panel',
23509         collapsible: true,
23510         split: true,
23511         width: 200,
23512         xtype: 'grid',
23513         // remaining grid configuration not shown ...
23514         // notice that the GridPanel is added directly as the region
23515         // it is not "overnested" inside another Panel
23516     }, {
23517         region: 'center',
23518         xtype: 'tabpanel', // TabPanel itself has no title
23519         items: {
23520             title: 'Default Tab',
23521             html: 'The first tab\'s content. Others may be added dynamically'
23522         }
23523     }]
23524 });
23525 </code></pre>
23526  * @constructor
23527  * Create a new Viewport
23528  * @param {Object} config The config object
23529  * @xtype viewport
23530  */
23531 Ext.Viewport = Ext.extend(Ext.Container, {
23532     /*
23533      * Privatize config options which, if used, would interfere with the
23534      * correct operation of the Viewport as the sole manager of the
23535      * layout of the document body.
23536      */
23537     /**
23538      * @cfg {Mixed} applyTo @hide
23539      */
23540     /**
23541      * @cfg {Boolean} allowDomMove @hide
23542      */
23543     /**
23544      * @cfg {Boolean} hideParent @hide
23545      */
23546     /**
23547      * @cfg {Mixed} renderTo @hide
23548      */
23549     /**
23550      * @cfg {Boolean} hideParent @hide
23551      */
23552     /**
23553      * @cfg {Number} height @hide
23554      */
23555     /**
23556      * @cfg {Number} width @hide
23557      */
23558     /**
23559      * @cfg {Boolean} autoHeight @hide
23560      */
23561     /**
23562      * @cfg {Boolean} autoWidth @hide
23563      */
23564     /**
23565      * @cfg {Boolean} deferHeight @hide
23566      */
23567     /**
23568      * @cfg {Boolean} monitorResize @hide
23569      */
23570
23571     initComponent : function() {
23572         Ext.Viewport.superclass.initComponent.call(this);
23573         document.getElementsByTagName('html')[0].className += ' x-viewport';
23574         this.el = Ext.getBody();
23575         this.el.setHeight = Ext.emptyFn;
23576         this.el.setWidth = Ext.emptyFn;
23577         this.el.setSize = Ext.emptyFn;
23578         this.el.dom.scroll = 'no';
23579         this.allowDomMove = false;
23580         this.autoWidth = true;
23581         this.autoHeight = true;
23582         Ext.EventManager.onWindowResize(this.fireResize, this);
23583         this.renderTo = this.el;
23584     },
23585
23586     fireResize : function(w, h){
23587         this.fireEvent('resize', this, w, h, w, h);
23588     }
23589 });
23590 Ext.reg('viewport', Ext.Viewport);
23591 /**
23592  * @class Ext.Panel
23593  * @extends Ext.Container
23594  * <p>Panel is a container that has specific functionality and structural components that make
23595  * it the perfect building block for application-oriented user interfaces.</p>
23596  * <p>Panels are, by virtue of their inheritance from {@link Ext.Container}, capable
23597  * of being configured with a {@link Ext.Container#layout layout}, and containing child Components.</p>
23598  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.Container#add adding} Components
23599  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
23600  * 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
23601  * default, Panels use the {@link Ext.layout.ContainerLayout ContainerLayout} scheme. This simply renders
23602  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
23603  * at all.</p>
23604  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
23605  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
23606  * information).</p>
23607  * <p>Panel also provides built-in {@link #collapsible expandable and collapsible behavior}, along with
23608  * a variety of {@link #tools prebuilt tool buttons} that can be wired up to provide other customized
23609  * behavior.  Panels can be easily dropped into any {@link Ext.Container Container} or layout, and the
23610  * layout and rendering pipeline is {@link Ext.Container#add completely managed by the framework}.</p>
23611  * @constructor
23612  * @param {Object} config The config object
23613  * @xtype panel
23614  */
23615 Ext.Panel = Ext.extend(Ext.Container, {
23616     /**
23617      * The Panel's header {@link Ext.Element Element}. Read-only.
23618      * <p>This Element is used to house the {@link #title} and {@link #tools}</p>
23619      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
23620      * @type Ext.Element
23621      * @property header
23622      */
23623     /**
23624      * The Panel's body {@link Ext.Element Element} which may be used to contain HTML content.
23625      * The content may be specified in the {@link #html} config, or it may be loaded using the
23626      * {@link autoLoad} config, or through the Panel's {@link #getUpdater Updater}. Read-only.
23627      * <p>If this is used to load visible HTML elements in either way, then
23628      * the Panel may not be used as a Layout for hosting nested Panels.</p>
23629      * <p>If this Panel is intended to be used as the host of a Layout (See {@link #layout}
23630      * then the body Element must not be loaded or changed - it is under the control
23631      * of the Panel's Layout.
23632      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
23633      * @type Ext.Element
23634      * @property body
23635      */
23636     /**
23637      * The Panel's bwrap {@link Ext.Element Element} used to contain other Panel elements
23638      * (tbar, body, bbar, footer). See {@link #bodyCfg}. Read-only.
23639      * @type Ext.Element
23640      * @property bwrap
23641      */
23642     /**
23643      * True if this panel is collapsed. Read-only.
23644      * @type Boolean
23645      * @property collapsed
23646      */
23647     /**
23648      * @cfg {Object} bodyCfg
23649      * <p>A {@link Ext.DomHelper DomHelper} element specification object may be specified for any
23650      * Panel Element.</p>
23651      * <p>By default, the Default element in the table below will be used for the html markup to
23652      * create a child element with the commensurate Default class name (<code>baseCls</code> will be
23653      * replaced by <code>{@link #baseCls}</code>):</p>
23654      * <pre>
23655      * Panel      Default  Default             Custom      Additional       Additional
23656      * Element    element  class               element     class            style
23657      * ========   ==========================   =========   ==============   ===========
23658      * {@link #header}     div      {@link #baseCls}+'-header'   {@link #headerCfg}   headerCssClass   headerStyle
23659      * {@link #bwrap}      div      {@link #baseCls}+'-bwrap'     {@link #bwrapCfg}    bwrapCssClass    bwrapStyle
23660      * + tbar     div      {@link #baseCls}+'-tbar'       {@link #tbarCfg}     tbarCssClass     tbarStyle
23661      * + {@link #body}     div      {@link #baseCls}+'-body'       {@link #bodyCfg}     {@link #bodyCssClass}     {@link #bodyStyle}
23662      * + bbar     div      {@link #baseCls}+'-bbar'       {@link #bbarCfg}     bbarCssClass     bbarStyle
23663      * + {@link #footer}   div      {@link #baseCls}+'-footer'   {@link #footerCfg}   footerCssClass   footerStyle
23664      * </pre>
23665      * <p>Configuring a Custom element may be used, for example, to force the {@link #body} Element
23666      * to use a different form of markup than is created by default. An example of this might be
23667      * to {@link Ext.Element#createChild create a child} Panel containing a custom content, such as
23668      * a header, or forcing centering of all Panel content by having the body be a &lt;center&gt;
23669      * element:</p>
23670      * <pre><code>
23671 new Ext.Panel({
23672     title: 'Message Title',
23673     renderTo: Ext.getBody(),
23674     width: 200, height: 130,
23675     <b>bodyCfg</b>: {
23676         tag: 'center',
23677         cls: 'x-panel-body',  // Default class not applied if Custom element specified
23678         html: 'Message'
23679     },
23680     footerCfg: {
23681         tag: 'h2',
23682         cls: 'x-panel-footer'        // same as the Default class
23683         html: 'footer html'
23684     },
23685     footerCssClass: 'custom-footer', // additional css class, see {@link Ext.element#addClass addClass}
23686     footerStyle:    'background-color:red' // see {@link #bodyStyle}
23687 });
23688      * </code></pre>
23689      * <p>The example above also explicitly creates a <code>{@link #footer}</code> with custom markup and
23690      * styling applied.</p>
23691      */
23692     /**
23693      * @cfg {Object} headerCfg
23694      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23695      * of this Panel's {@link #header} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23696      */
23697     /**
23698      * @cfg {Object} bwrapCfg
23699      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23700      * of this Panel's {@link #bwrap} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23701      */
23702     /**
23703      * @cfg {Object} tbarCfg
23704      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23705      * of this Panel's {@link #tbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23706      */
23707     /**
23708      * @cfg {Object} bbarCfg
23709      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23710      * of this Panel's {@link #bbar} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23711      */
23712     /**
23713      * @cfg {Object} footerCfg
23714      * <p>A {@link Ext.DomHelper DomHelper} element specification object specifying the element structure
23715      * of this Panel's {@link #footer} Element.  See <code>{@link #bodyCfg}</code> also.</p>
23716      */
23717     /**
23718      * @cfg {Boolean} closable
23719      * Panels themselves do not directly support being closed, but some Panel subclasses do (like
23720      * {@link Ext.Window}) or a Panel Class within an {@link Ext.TabPanel}.  Specify <code>true</code>
23721      * to enable closing in such situations. Defaults to <code>false</code>.
23722      */
23723     /**
23724      * The Panel's footer {@link Ext.Element Element}. Read-only.
23725      * <p>This Element is used to house the Panel's <code>{@link #buttons}</code> or <code>{@link #fbar}</code>.</p>
23726      * <br><p><b>Note</b>: see the Note for <code>{@link Ext.Component#el el}</code> also.</p>
23727      * @type Ext.Element
23728      * @property footer
23729      */
23730     /**
23731      * @cfg {Mixed} applyTo
23732      * <p>The id of the node, a DOM node or an existing Element corresponding to a DIV that is already present in
23733      * the document that specifies some panel-specific structural markup.  When <code>applyTo</code> is used,
23734      * constituent parts of the panel can be specified by CSS class name within the main element, and the panel
23735      * will automatically create those components from that markup. Any required components not specified in the
23736      * markup will be autogenerated if necessary.</p>
23737      * <p>The following class names are supported (baseCls will be replaced by {@link #baseCls}):</p>
23738      * <ul><li>baseCls + '-header'</li>
23739      * <li>baseCls + '-header-text'</li>
23740      * <li>baseCls + '-bwrap'</li>
23741      * <li>baseCls + '-tbar'</li>
23742      * <li>baseCls + '-body'</li>
23743      * <li>baseCls + '-bbar'</li>
23744      * <li>baseCls + '-footer'</li></ul>
23745      * <p>Using this config, a call to render() is not required.  If applyTo is specified, any value passed for
23746      * {@link #renderTo} will be ignored and the target element's parent node will automatically be used as the
23747      * panel's container.</p>
23748      */
23749     /**
23750      * @cfg {Object/Array} tbar
23751      * <p>The top toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
23752      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
23753      * To access the top toolbar after render, use {@link #getTopToolbar}.</p>
23754      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23755      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23756      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23757      * submission parameters are collected from the DOM tree.</p>
23758      */
23759     /**
23760      * @cfg {Object/Array} bbar
23761      * <p>The bottom toolbar of the panel. This can be a {@link Ext.Toolbar} object, a toolbar config, or an array of
23762      * buttons/button configs to be added to the toolbar.  Note that this is not available as a property after render.
23763      * To access the bottom toolbar after render, use {@link #getBottomToolbar}.</p>
23764      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23765      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23766      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23767      * submission parameters are collected from the DOM tree.</p>
23768      */
23769     /** @cfg {Object/Array} fbar
23770      * <p>A {@link Ext.Toolbar Toolbar} object, a Toolbar config, or an array of
23771      * {@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>
23772      * <p>After render, the <code>fbar</code> property will be an {@link Ext.Toolbar Toolbar} instance.</p>
23773      * <p>If <code>{@link #buttons}</code> are specified, they will supersede the <code>fbar</code> configuration property.</p>
23774      * The Panel's <code>{@link #buttonAlign}</code> configuration affects the layout of these items, for example:
23775      * <pre><code>
23776 var w = new Ext.Window({
23777     height: 250,
23778     width: 500,
23779     bbar: new Ext.Toolbar({
23780         items: [{
23781             text: 'bbar Left'
23782         },'->',{
23783             text: 'bbar Right'
23784         }]
23785     }),
23786     {@link #buttonAlign}: 'left', // anything but 'center' or 'right' and you can use '-', and '->'
23787                                   // to control the alignment of fbar items
23788     fbar: [{
23789         text: 'fbar Left'
23790     },'->',{
23791         text: 'fbar Right'
23792     }]
23793 }).show();
23794      * </code></pre>
23795      * <p><b>Note:</b> Although a Toolbar may contain Field components, these will <b>not</b> be updated by a load
23796      * of an ancestor FormPanel. A Panel's toolbars are not part of the standard Container->Component hierarchy, and
23797      * so are not scanned to collect form items. However, the values <b>will</b> be submitted because form
23798      * submission parameters are collected from the DOM tree.</p>
23799      */
23800     /**
23801      * @cfg {Boolean} header
23802      * <code>true</code> to create the Panel's header element explicitly, <code>false</code> to skip creating
23803      * it.  If a <code>{@link #title}</code> is set the header will be created automatically, otherwise it will not.
23804      * If a <code>{@link #title}</code> is set but <code>header</code> is explicitly set to <code>false</code>, the header
23805      * will not be rendered.
23806      */
23807     /**
23808      * @cfg {Boolean} footer
23809      * <code>true</code> to create the footer element explicitly, false to skip creating it. The footer
23810      * will be created automatically if <code>{@link #buttons}</code> or a <code>{@link #fbar}</code> have
23811      * been configured.  See <code>{@link #bodyCfg}</code> for an example.
23812      */
23813     /**
23814      * @cfg {String} title
23815      * The title text to be used as innerHTML (html tags are accepted) to display in the panel
23816      * <code>{@link #header}</code> (defaults to ''). When a <code>title</code> is specified the
23817      * <code>{@link #header}</code> element will automatically be created and displayed unless
23818      * {@link #header} is explicitly set to <code>false</code>.  If you do not want to specify a
23819      * <code>title</code> at config time, but you may want one later, you must either specify a non-empty
23820      * <code>title</code> (a blank space ' ' will do) or <code>header:true</code> so that the container
23821      * element will get created.
23822      */
23823     /**
23824      * @cfg {Array} buttons
23825      * <code>buttons</code> will be used as <code>{@link Ext.Container#items items}</code> for the toolbar in
23826      * the footer (<code>{@link #fbar}</code>). Typically the value of this configuration property will be
23827      * an array of {@link Ext.Button}s or {@link Ext.Button} configuration objects.
23828      * If an item is configured with <code>minWidth</code> or the Panel is configured with <code>minButtonWidth</code>,
23829      * that width will be applied to the item.
23830      */
23831     /**
23832      * @cfg {Object/String/Function} autoLoad
23833      * A valid url spec according to the Updater {@link Ext.Updater#update} method.
23834      * If autoLoad is not null, the panel will attempt to load its contents
23835      * immediately upon render.<p>
23836      * The URL will become the default URL for this panel's {@link #body} element,
23837      * so it may be {@link Ext.Element#refresh refresh}ed at any time.</p>
23838      */
23839     /**
23840      * @cfg {Boolean} frame
23841      * <code>false</code> by default to render with plain 1px square borders. <code>true</code> to render with
23842      * 9 elements, complete with custom rounded corners (also see {@link Ext.Element#boxWrap}).
23843      * <p>The template generated for each condition is depicted below:</p><pre><code>
23844      *
23845 // frame = false
23846 &lt;div id="developer-specified-id-goes-here" class="x-panel">
23847
23848     &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:false)&lt;/span>&lt;/div>
23849
23850     &lt;div class="x-panel-bwrap">
23851         &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
23852     &lt;/div>
23853 &lt;/div>
23854
23855 // frame = true (create 9 elements)
23856 &lt;div id="developer-specified-id-goes-here" class="x-panel">
23857     &lt;div class="x-panel-tl">&lt;div class="x-panel-tr">&lt;div class="x-panel-tc">
23858         &lt;div class="x-panel-header">&lt;span class="x-panel-header-text">Title: (frame:true)&lt;/span>&lt;/div>
23859     &lt;/div>&lt;/div>&lt;/div>
23860
23861     &lt;div class="x-panel-bwrap">
23862         &lt;div class="x-panel-ml">&lt;div class="x-panel-mr">&lt;div class="x-panel-mc">
23863             &lt;div class="x-panel-body">&lt;p>html value goes here&lt;/p>&lt;/div>
23864         &lt;/div>&lt;/div>&lt;/div>
23865
23866         &lt;div class="x-panel-bl">&lt;div class="x-panel-br">&lt;div class="x-panel-bc"/>
23867         &lt;/div>&lt;/div>&lt;/div>
23868 &lt;/div>
23869      * </code></pre>
23870      */
23871     /**
23872      * @cfg {Boolean} border
23873      * True to display the borders of the panel's body element, false to hide them (defaults to true).  By default,
23874      * the border is a 2px wide inset border, but this can be further altered by setting {@link #bodyBorder} to false.
23875      */
23876     /**
23877      * @cfg {Boolean} bodyBorder
23878      * True to display an interior border on the body element of the panel, false to hide it (defaults to true).
23879      * This only applies when {@link #border} == true.  If border == true and bodyBorder == false, the border will display
23880      * as a 1px wide inset border, giving the entire body element an inset appearance.
23881      */
23882     /**
23883      * @cfg {String/Object/Function} bodyCssClass
23884      * Additional css class selector to be applied to the {@link #body} element in the format expected by
23885      * {@link Ext.Element#addClass} (defaults to null). See {@link #bodyCfg}.
23886      */
23887     /**
23888      * @cfg {String/Object/Function} bodyStyle
23889      * Custom CSS styles to be applied to the {@link #body} element in the format expected by
23890      * {@link Ext.Element#applyStyles} (defaults to null). See {@link #bodyCfg}.
23891      */
23892     /**
23893      * @cfg {String} iconCls
23894      * The CSS class selector that specifies a background image to be used as the header icon (defaults to '').
23895      * <p>An example of specifying a custom icon class would be something like:
23896      * </p><pre><code>
23897 // specify the property in the config for the class:
23898      ...
23899      iconCls: 'my-icon'
23900
23901 // css class that specifies background image to be used as the icon image:
23902 .my-icon { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
23903 </code></pre>
23904      */
23905     /**
23906      * @cfg {Boolean} collapsible
23907      * True to make the panel collapsible and have the expand/collapse toggle button automatically rendered into
23908      * the header tool button area, false to keep the panel statically sized with no button (defaults to false).
23909      */
23910     /**
23911      * @cfg {Array} tools
23912      * An array of tool button configs to be added to the header tool area. When rendered, each tool is
23913      * stored as an {@link Ext.Element Element} referenced by a public property called <code><b></b>tools.<i>&lt;tool-type&gt;</i></code>
23914      * <p>Each tool config may contain the following properties:
23915      * <div class="mdetail-params"><ul>
23916      * <li><b>id</b> : String<div class="sub-desc"><b>Required.</b> The type
23917      * 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
23918      * resulting tool Element. Ext provides CSS rules, and an icon sprite containing images for the tool types listed below.
23919      * The developer may implement custom tools by supplying alternate CSS rules and background images:
23920      * <ul>
23921      * <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>
23922      * <div class="x-tool x-tool-close" style="float:left; margin-right:5;"> </div><div><code> close</code></div>
23923      * <div class="x-tool x-tool-minimize" style="float:left; margin-right:5;"> </div><div><code> minimize</code></div>
23924      * <div class="x-tool x-tool-maximize" style="float:left; margin-right:5;"> </div><div><code> maximize</code></div>
23925      * <div class="x-tool x-tool-restore" style="float:left; margin-right:5;"> </div><div><code> restore</code></div>
23926      * <div class="x-tool x-tool-gear" style="float:left; margin-right:5;"> </div><div><code> gear</code></div>
23927      * <div class="x-tool x-tool-pin" style="float:left; margin-right:5;"> </div><div><code> pin</code></div>
23928      * <div class="x-tool x-tool-unpin" style="float:left; margin-right:5;"> </div><div><code> unpin</code></div>
23929      * <div class="x-tool x-tool-right" style="float:left; margin-right:5;"> </div><div><code> right</code></div>
23930      * <div class="x-tool x-tool-left" style="float:left; margin-right:5;"> </div><div><code> left</code></div>
23931      * <div class="x-tool x-tool-up" style="float:left; margin-right:5;"> </div><div><code> up</code></div>
23932      * <div class="x-tool x-tool-down" style="float:left; margin-right:5;"> </div><div><code> down</code></div>
23933      * <div class="x-tool x-tool-refresh" style="float:left; margin-right:5;"> </div><div><code> refresh</code></div>
23934      * <div class="x-tool x-tool-minus" style="float:left; margin-right:5;"> </div><div><code> minus</code></div>
23935      * <div class="x-tool x-tool-plus" style="float:left; margin-right:5;"> </div><div><code> plus</code></div>
23936      * <div class="x-tool x-tool-help" style="float:left; margin-right:5;"> </div><div><code> help</code></div>
23937      * <div class="x-tool x-tool-search" style="float:left; margin-right:5;"> </div><div><code> search</code></div>
23938      * <div class="x-tool x-tool-save" style="float:left; margin-right:5;"> </div><div><code> save</code></div>
23939      * <div class="x-tool x-tool-print" style="float:left; margin-right:5;"> </div><div><code> print</code></div>
23940      * </ul></div></li>
23941      * <li><b>handler</b> : Function<div class="sub-desc"><b>Required.</b> The function to
23942      * call when clicked. Arguments passed are:<ul>
23943      * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
23944      * <li><b>toolEl</b> : Ext.Element<div class="sub-desc">The tool Element.</div></li>
23945      * <li><b>panel</b> : Ext.Panel<div class="sub-desc">The host Panel</div></li>
23946      * <li><b>tc</b> : Object<div class="sub-desc">The tool configuration object</div></li>
23947      * </ul></div></li>
23948      * <li><b>stopEvent</b> : Boolean<div class="sub-desc">Defaults to true. Specify as false to allow click event to propagate.</div></li>
23949      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the handler.</div></li>
23950      * <li><b>qtip</b> : String/Object<div class="sub-desc">A tip string, or
23951      * a config argument to {@link Ext.QuickTip#register}</div></li>
23952      * <li><b>hidden</b> : Boolean<div class="sub-desc">True to initially render hidden.</div></li>
23953      * <li><b>on</b> : Object<div class="sub-desc">A listener config object specifiying
23954      * event listeners in the format of an argument to {@link #addListener}</div></li>
23955      * </ul></div>
23956      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
23957      * tools only provide the visual button. Any required functionality must be provided by adding
23958      * handlers that implement the necessary behavior.</p>
23959      * <p>Example usage:</p>
23960      * <pre><code>
23961 tools:[{
23962     id:'refresh',
23963     qtip: 'Refresh form Data',
23964     // hidden:true,
23965     handler: function(event, toolEl, panel){
23966         // refresh logic
23967     }
23968 },
23969 {
23970     id:'help',
23971     qtip: 'Get Help',
23972     handler: function(event, toolEl, panel){
23973         // whatever
23974     }
23975 }]
23976 </code></pre>
23977      * <p>For the custom id of <code>'help'</code> define two relevant css classes with a link to
23978      * a 15x15 image:</p>
23979      * <pre><code>
23980 .x-tool-help {background-image: url(images/help.png);}
23981 .x-tool-help-over {background-image: url(images/help_over.png);}
23982 // if using an image sprite:
23983 .x-tool-help {background-image: url(images/help.png) no-repeat 0 0;}
23984 .x-tool-help-over {background-position:-15px 0;}
23985 </code></pre>
23986      */
23987     /**
23988      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
23989      * <p>A Template used to create {@link #tools} in the {@link #header} Element. Defaults to:</p><pre><code>
23990 new Ext.Template('&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>')</code></pre>
23991      * <p>This may may be overridden to provide a custom DOM structure for tools based upon a more
23992      * complex XTemplate. The template's data is a single tool configuration object (Not the entire Array)
23993      * as specified in {@link #tools}.  In the following example an &lt;a> tag is used to provide a
23994      * visual indication when hovering over the tool:</p><pre><code>
23995 var win = new Ext.Window({
23996     tools: [{
23997         id: 'download',
23998         href: '/MyPdfDoc.pdf'
23999     }],
24000     toolTemplate: new Ext.XTemplate(
24001         '&lt;tpl if="id==\'download\'">',
24002             '&lt;a class="x-tool x-tool-pdf" href="{href}">&lt;/a>',
24003         '&lt;/tpl>',
24004         '&lt;tpl if="id!=\'download\'">',
24005             '&lt;div class="x-tool x-tool-{id}">&amp;#160;&lt;/div>',
24006         '&lt;/tpl>'
24007     ),
24008     width:500,
24009     height:300,
24010     closeAction:'hide'
24011 });</code></pre>
24012      * <p>Note that the CSS class 'x-tool-pdf' should have an associated style rule which provides an
24013      * appropriate background image, something like:</p>
24014     <pre><code>
24015     a.x-tool-pdf {background-image: url(../shared/extjs/images/pdf.gif)!important;}
24016     </code></pre>
24017      */
24018     /**
24019      * @cfg {Boolean} hideCollapseTool
24020      * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
24021      * <code>false</code> to display it (defaults to <code>false</code>).
24022      */
24023     /**
24024      * @cfg {Boolean} titleCollapse
24025      * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
24026      * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
24027      * (defaults to <code>false</code>)). If this panel is a child item of a border layout also see the
24028      * {@link Ext.layout.BorderLayout.Region BorderLayout.Region}
24029      * <code>{@link Ext.layout.BorderLayout.Region#floatable floatable}</code> config option.
24030      */
24031
24032     /**
24033      * @cfg {Mixed} floating
24034      * <p>This property is used to configure the underlying {@link Ext.Layer}. Acceptable values for this
24035      * configuration property are:</p><div class="mdetail-params"><ul>
24036      * <li><b><code>false</code></b> : <b>Default.</b><div class="sub-desc">Display the panel inline where it is
24037      * rendered.</div></li>
24038      * <li><b><code>true</code></b> : <div class="sub-desc">Float the panel (absolute position it with automatic
24039      * shimming and shadow).<ul>
24040      * <div class="sub-desc">Setting floating to true will create an Ext.Layer for this panel and display the
24041      * panel at negative offsets so that it is hidden.</div>
24042      * <div class="sub-desc">Since the panel will be absolute positioned, the position must be set explicitly
24043      * <i>after</i> render (e.g., <code>myPanel.setPosition(100,100);</code>).</div>
24044      * <div class="sub-desc"><b>Note</b>: when floating a panel you should always assign a fixed width,
24045      * otherwise it will be auto width and will expand to fill to the right edge of the viewport.</div>
24046      * </ul></div></li>
24047      * <li><b><code>{@link Ext.Layer object}</code></b> : <div class="sub-desc">The specified object will be used
24048      * as the configuration object for the {@link Ext.Layer} that will be created.</div></li>
24049      * </ul></div>
24050      */
24051     /**
24052      * @cfg {Boolean/String} shadow
24053      * <code>true</code> (or a valid Ext.Shadow {@link Ext.Shadow#mode} value) to display a shadow behind the
24054      * panel, <code>false</code> to display no shadow (defaults to <code>'sides'</code>).  Note that this option
24055      * only applies when <code>{@link #floating} = true</code>.
24056      */
24057     /**
24058      * @cfg {Number} shadowOffset
24059      * The number of pixels to offset the shadow if displayed (defaults to <code>4</code>). Note that this
24060      * option only applies when <code>{@link #floating} = true</code>.
24061      */
24062     /**
24063      * @cfg {Boolean} shim
24064      * <code>false</code> to disable the iframe shim in browsers which need one (defaults to <code>true</code>).
24065      * Note that this option only applies when <code>{@link #floating} = true</code>.
24066      */
24067     /**
24068      * @cfg {Object/Array} keys
24069      * A {@link Ext.KeyMap} config object (in the format expected by {@link Ext.KeyMap#addBinding}
24070      * used to assign custom key handling to this panel (defaults to <code>null</code>).
24071      */
24072     /**
24073      * @cfg {Boolean/Object} draggable
24074      * <p><code>true</code> to enable dragging of this Panel (defaults to <code>false</code>).</p>
24075      * <p>For custom drag/drop implementations, an <b>Ext.Panel.DD</b> config could also be passed
24076      * in this config instead of <code>true</code>. Ext.Panel.DD is an internal, undocumented class which
24077      * moves a proxy Element around in place of the Panel's element, but provides no other behaviour
24078      * during dragging or on drop. It is a subclass of {@link Ext.dd.DragSource}, so behaviour may be
24079      * added by implementing the interface methods of {@link Ext.dd.DragDrop} e.g.:
24080      * <pre><code>
24081 new Ext.Panel({
24082     title: 'Drag me',
24083     x: 100,
24084     y: 100,
24085     renderTo: Ext.getBody(),
24086     floating: true,
24087     frame: true,
24088     width: 400,
24089     height: 200,
24090     draggable: {
24091 //      Config option of Ext.Panel.DD class.
24092 //      It&#39;s a floating Panel, so do not show a placeholder proxy in the original position.
24093         insertProxy: false,
24094
24095 //      Called for each mousemove event while dragging the DD object.
24096         onDrag : function(e){
24097 //          Record the x,y position of the drag proxy so that we can
24098 //          position the Panel at end of drag.
24099             var pel = this.proxy.getEl();
24100             this.x = pel.getLeft(true);
24101             this.y = pel.getTop(true);
24102
24103 //          Keep the Shadow aligned if there is one.
24104             var s = this.panel.getEl().shadow;
24105             if (s) {
24106                 s.realign(this.x, this.y, pel.getWidth(), pel.getHeight());
24107             }
24108         },
24109
24110 //      Called on the mouseup event.
24111         endDrag : function(e){
24112             this.panel.setPosition(this.x, this.y);
24113         }
24114     }
24115 }).show();
24116 </code></pre>
24117      */
24118     /**
24119      * @cfg {Boolean} disabled
24120      * Render this panel disabled (default is <code>false</code>). An important note when using the disabled
24121      * config on panels is that IE will often fail to initialize the disabled mask element correectly if
24122      * the panel's layout has not yet completed by the time the Panel is disabled during the render process.
24123      * If you experience this issue, you may need to instead use the {@link #afterlayout} event to initialize
24124      * the disabled state:
24125      * <pre><code>
24126 new Ext.Panel({
24127     ...
24128     listeners: {
24129         'afterlayout': {
24130             fn: function(p){
24131                 p.disable();
24132             },
24133             single: true // important, as many layouts can occur
24134         }
24135     }
24136 });
24137 </code></pre>
24138      */
24139     /**
24140      * @cfg {Boolean} autoHeight
24141      * <code>true</code> to use height:'auto', <code>false</code> to use fixed height (defaults to <code>false</code>).
24142      * <b>Note</b>: Setting <code>autoHeight: true</code> means that the browser will manage the panel's height
24143      * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that
24144      * manages dimensions (<code>fit</code>, <code>border</code>, etc.) then setting <code>autoHeight: true</code>
24145      * can cause issues with scrolling and will not generally work as expected since the panel will take
24146      * on the height of its contents rather than the height required by the Ext layout.
24147      */
24148
24149
24150     /**
24151      * @cfg {String} baseCls
24152      * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
24153      * <p>Another option available by default is to specify <code>'x-plain'</code> which strips all styling
24154      * except for required attributes for Ext layouts to function (e.g. overflow:hidden).
24155      * See <code>{@link #unstyled}</code> also.</p>
24156      */
24157     baseCls : 'x-panel',
24158     /**
24159      * @cfg {String} collapsedCls
24160      * A CSS class to add to the panel's element after it has been collapsed (defaults to
24161      * <code>'x-panel-collapsed'</code>).
24162      */
24163     collapsedCls : 'x-panel-collapsed',
24164     /**
24165      * @cfg {Boolean} maskDisabled
24166      * <code>true</code> to mask the panel when it is {@link #disabled}, <code>false</code> to not mask it (defaults
24167      * to <code>true</code>).  Either way, the panel will always tell its contained elements to disable themselves
24168      * when it is disabled, but masking the panel can provide an additional visual cue that the panel is
24169      * disabled.
24170      */
24171     maskDisabled : true,
24172     /**
24173      * @cfg {Boolean} animCollapse
24174      * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
24175      * animation (defaults to <code>true</code> if the {@link Ext.Fx} class is available, otherwise <code>false</code>).
24176      */
24177     animCollapse : Ext.enableFx,
24178     /**
24179      * @cfg {Boolean} headerAsText
24180      * <code>true</code> to display the panel <code>{@link #title}</code> in the <code>{@link #header}</code>,
24181      * <code>false</code> to hide it (defaults to <code>true</code>).
24182      */
24183     headerAsText : true,
24184     /**
24185      * @cfg {String} buttonAlign
24186      * The alignment of any {@link #buttons} added to this panel.  Valid values are <code>'right'</code>,
24187      * <code>'left'</code> and <code>'center'</code> (defaults to <code>'right'</code>).
24188      */
24189     buttonAlign : 'right',
24190     /**
24191      * @cfg {Boolean} collapsed
24192      * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
24193      * <code>false</code>).
24194      */
24195     collapsed : false,
24196     /**
24197      * @cfg {Boolean} collapseFirst
24198      * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
24199      * any other tools in the panel's title bar, <code>false</code> to render it last (defaults to <code>true</code>).
24200      */
24201     collapseFirst : true,
24202     /**
24203      * @cfg {Number} minButtonWidth
24204      * Minimum width in pixels of all {@link #buttons} in this panel (defaults to <code>75</code>)
24205      */
24206     minButtonWidth : 75,
24207     /**
24208      * @cfg {Boolean} unstyled
24209      * Overrides the <code>{@link #baseCls}</code> setting to <code>{@link #baseCls} = 'x-plain'</code> which renders
24210      * the panel unstyled except for required attributes for Ext layouts to function (e.g. overflow:hidden).
24211      */
24212     /**
24213      * @cfg {String} elements
24214      * A comma-delimited list of panel elements to initialize when the panel is rendered.  Normally, this list will be
24215      * generated automatically based on the items added to the panel at config time, but sometimes it might be useful to
24216      * make sure a structural element is rendered even if not specified at config time (for example, you may want
24217      * to add a button or toolbar dynamically after the panel has been rendered).  Adding those elements to this
24218      * list will allocate the required placeholders in the panel when it is rendered.  Valid values are<div class="mdetail-params"><ul>
24219      * <li><code>header</code></li>
24220      * <li><code>tbar</code> (top bar)</li>
24221      * <li><code>body</code></li>
24222      * <li><code>bbar</code> (bottom bar)</li>
24223      * <li><code>footer</code></li>
24224      * </ul></div>
24225      * Defaults to '<code>body</code>'.
24226      */
24227     elements : 'body',
24228     /**
24229      * @cfg {Boolean} preventBodyReset
24230      * Defaults to <code>false</code>.  When set to <code>true</code>, an extra css class <code>'x-panel-normal'</code>
24231      * will be added to the panel's element, effectively applying css styles suggested by the W3C
24232      * (see http://www.w3.org/TR/CSS21/sample.html) to the Panel's <b>body</b> element (not the header,
24233      * footer, etc.).
24234      */
24235     preventBodyReset : false,
24236
24237     /**
24238      * @cfg {Number/String} padding
24239      * A shortcut for setting a padding style on the body element. The value can either be
24240      * a number to be applied to all sides, or a normal css string describing padding.
24241      * Defaults to <tt>undefined</tt>.
24242      *
24243      */
24244     padding: undefined,
24245
24246     /** @cfg {String} resizeEvent
24247      * The event to listen to for resizing in layouts. Defaults to <tt>'bodyresize'</tt>.
24248      */
24249     resizeEvent: 'bodyresize',
24250
24251     // protected - these could be used to customize the behavior of the window,
24252     // but changing them would not be useful without further mofifications and
24253     // could lead to unexpected or undesirable results.
24254     toolTarget : 'header',
24255     collapseEl : 'bwrap',
24256     slideAnchor : 't',
24257     disabledClass : '',
24258
24259     // private, notify box this class will handle heights
24260     deferHeight : true,
24261     // private
24262     expandDefaults: {
24263         duration : 0.25
24264     },
24265     // private
24266     collapseDefaults : {
24267         duration : 0.25
24268     },
24269
24270     // private
24271     initComponent : function(){
24272         Ext.Panel.superclass.initComponent.call(this);
24273
24274         this.addEvents(
24275             /**
24276              * @event bodyresize
24277              * Fires after the Panel has been resized.
24278              * @param {Ext.Panel} p the Panel which has been resized.
24279              * @param {Number} width The Panel body's new width.
24280              * @param {Number} height The Panel body's new height.
24281              */
24282             'bodyresize',
24283             /**
24284              * @event titlechange
24285              * Fires after the Panel title has been {@link #title set} or {@link #setTitle changed}.
24286              * @param {Ext.Panel} p the Panel which has had its title changed.
24287              * @param {String} The new title.
24288              */
24289             'titlechange',
24290             /**
24291              * @event iconchange
24292              * Fires after the Panel icon class has been {@link #iconCls set} or {@link #setIconClass changed}.
24293              * @param {Ext.Panel} p the Panel which has had its {@link #iconCls icon class} changed.
24294              * @param {String} The new icon class.
24295              * @param {String} The old icon class.
24296              */
24297             'iconchange',
24298             /**
24299              * @event collapse
24300              * Fires after the Panel has been collapsed.
24301              * @param {Ext.Panel} p the Panel that has been collapsed.
24302              */
24303             'collapse',
24304             /**
24305              * @event expand
24306              * Fires after the Panel has been expanded.
24307              * @param {Ext.Panel} p The Panel that has been expanded.
24308              */
24309             'expand',
24310             /**
24311              * @event beforecollapse
24312              * Fires before the Panel is collapsed.  A handler can return false to cancel the collapse.
24313              * @param {Ext.Panel} p the Panel being collapsed.
24314              * @param {Boolean} animate True if the collapse is animated, else false.
24315              */
24316             'beforecollapse',
24317             /**
24318              * @event beforeexpand
24319              * Fires before the Panel is expanded.  A handler can return false to cancel the expand.
24320              * @param {Ext.Panel} p The Panel being expanded.
24321              * @param {Boolean} animate True if the expand is animated, else false.
24322              */
24323             'beforeexpand',
24324             /**
24325              * @event beforeclose
24326              * Fires before the Panel is closed.  Note that Panels do not directly support being closed, but some
24327              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.  This event only
24328              * applies to such subclasses.
24329              * A handler can return false to cancel the close.
24330              * @param {Ext.Panel} p The Panel being closed.
24331              */
24332             'beforeclose',
24333             /**
24334              * @event close
24335              * Fires after the Panel is closed.  Note that Panels do not directly support being closed, but some
24336              * Panel subclasses do (like {@link Ext.Window}) or a Panel within a Ext.TabPanel.
24337              * @param {Ext.Panel} p The Panel that has been closed.
24338              */
24339             'close',
24340             /**
24341              * @event activate
24342              * Fires after the Panel has been visually activated.
24343              * Note that Panels do not directly support being activated, but some Panel subclasses
24344              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
24345              * activate and deactivate events under the control of the TabPanel.
24346              * @param {Ext.Panel} p The Panel that has been activated.
24347              */
24348             'activate',
24349             /**
24350              * @event deactivate
24351              * Fires after the Panel has been visually deactivated.
24352              * Note that Panels do not directly support being deactivated, but some Panel subclasses
24353              * do (like {@link Ext.Window}). Panels which are child Components of a TabPanel fire the
24354              * activate and deactivate events under the control of the TabPanel.
24355              * @param {Ext.Panel} p The Panel that has been deactivated.
24356              */
24357             'deactivate'
24358         );
24359
24360         if(this.unstyled){
24361             this.baseCls = 'x-plain';
24362         }
24363
24364
24365         this.toolbars = [];
24366         // shortcuts
24367         if(this.tbar){
24368             this.elements += ',tbar';
24369             this.topToolbar = this.createToolbar(this.tbar);
24370             this.tbar = null;
24371
24372         }
24373         if(this.bbar){
24374             this.elements += ',bbar';
24375             this.bottomToolbar = this.createToolbar(this.bbar);
24376             this.bbar = null;
24377         }
24378
24379         if(this.header === true){
24380             this.elements += ',header';
24381             this.header = null;
24382         }else if(this.headerCfg || (this.title && this.header !== false)){
24383             this.elements += ',header';
24384         }
24385
24386         if(this.footerCfg || this.footer === true){
24387             this.elements += ',footer';
24388             this.footer = null;
24389         }
24390
24391         if(this.buttons){
24392             this.fbar = this.buttons;
24393             this.buttons = null;
24394         }
24395         if(this.fbar){
24396             this.createFbar(this.fbar);
24397         }
24398         if(this.autoLoad){
24399             this.on('render', this.doAutoLoad, this, {delay:10});
24400         }
24401     },
24402
24403     // private
24404     createFbar : function(fbar){
24405         var min = this.minButtonWidth;
24406         this.elements += ',footer';
24407         this.fbar = this.createToolbar(fbar, {
24408             buttonAlign: this.buttonAlign,
24409             toolbarCls: 'x-panel-fbar',
24410             enableOverflow: false,
24411             defaults: function(c){
24412                 return {
24413                     minWidth: c.minWidth || min
24414                 };
24415             }
24416         });
24417         // @compat addButton and buttons could possibly be removed
24418         // @target 4.0
24419         /**
24420          * This Panel's Array of buttons as created from the <code>{@link #buttons}</code>
24421          * config property. Read only.
24422          * @type Array
24423          * @property buttons
24424          */
24425         this.fbar.items.each(function(c){
24426             c.minWidth = c.minWidth || this.minButtonWidth;
24427         }, this);
24428         this.buttons = this.fbar.items.items;
24429     },
24430
24431     // private
24432     createToolbar: function(tb, options){
24433         var result;
24434         // Convert array to proper toolbar config
24435         if(Ext.isArray(tb)){
24436             tb = {
24437                 items: tb
24438             };
24439         }
24440         result = tb.events ? Ext.apply(tb, options) : this.createComponent(Ext.apply({}, tb, options), 'toolbar');
24441         this.toolbars.push(result);
24442         return result;
24443     },
24444
24445     // private
24446     createElement : function(name, pnode){
24447         if(this[name]){
24448             pnode.appendChild(this[name].dom);
24449             return;
24450         }
24451
24452         if(name === 'bwrap' || this.elements.indexOf(name) != -1){
24453             if(this[name+'Cfg']){
24454                 this[name] = Ext.fly(pnode).createChild(this[name+'Cfg']);
24455             }else{
24456                 var el = document.createElement('div');
24457                 el.className = this[name+'Cls'];
24458                 this[name] = Ext.get(pnode.appendChild(el));
24459             }
24460             if(this[name+'CssClass']){
24461                 this[name].addClass(this[name+'CssClass']);
24462             }
24463             if(this[name+'Style']){
24464                 this[name].applyStyles(this[name+'Style']);
24465             }
24466         }
24467     },
24468
24469     // private
24470     onRender : function(ct, position){
24471         Ext.Panel.superclass.onRender.call(this, ct, position);
24472         this.createClasses();
24473
24474         var el = this.el,
24475             d = el.dom,
24476             bw,
24477             ts;
24478
24479
24480         if(this.collapsible && !this.hideCollapseTool){
24481             this.tools = this.tools ? this.tools.slice(0) : [];
24482             this.tools[this.collapseFirst?'unshift':'push']({
24483                 id: 'toggle',
24484                 handler : this.toggleCollapse,
24485                 scope: this
24486             });
24487         }
24488
24489         if(this.tools){
24490             ts = this.tools;
24491             this.elements += (this.header !== false) ? ',header' : '';
24492         }
24493         this.tools = {};
24494
24495         el.addClass(this.baseCls);
24496         if(d.firstChild){ // existing markup
24497             this.header = el.down('.'+this.headerCls);
24498             this.bwrap = el.down('.'+this.bwrapCls);
24499             var cp = this.bwrap ? this.bwrap : el;
24500             this.tbar = cp.down('.'+this.tbarCls);
24501             this.body = cp.down('.'+this.bodyCls);
24502             this.bbar = cp.down('.'+this.bbarCls);
24503             this.footer = cp.down('.'+this.footerCls);
24504             this.fromMarkup = true;
24505         }
24506         if (this.preventBodyReset === true) {
24507             el.addClass('x-panel-reset');
24508         }
24509         if(this.cls){
24510             el.addClass(this.cls);
24511         }
24512
24513         if(this.buttons){
24514             this.elements += ',footer';
24515         }
24516
24517         // This block allows for maximum flexibility and performance when using existing markup
24518
24519         // framing requires special markup
24520         if(this.frame){
24521             el.insertHtml('afterBegin', String.format(Ext.Element.boxMarkup, this.baseCls));
24522
24523             this.createElement('header', d.firstChild.firstChild.firstChild);
24524             this.createElement('bwrap', d);
24525
24526             // append the mid and bottom frame to the bwrap
24527             bw = this.bwrap.dom;
24528             var ml = d.childNodes[1], bl = d.childNodes[2];
24529             bw.appendChild(ml);
24530             bw.appendChild(bl);
24531
24532             var mc = bw.firstChild.firstChild.firstChild;
24533             this.createElement('tbar', mc);
24534             this.createElement('body', mc);
24535             this.createElement('bbar', mc);
24536             this.createElement('footer', bw.lastChild.firstChild.firstChild);
24537
24538             if(!this.footer){
24539                 this.bwrap.dom.lastChild.className += ' x-panel-nofooter';
24540             }
24541             /*
24542              * Store a reference to this element so:
24543              * a) We aren't looking it up all the time
24544              * b) The last element is reported incorrectly when using a loadmask
24545              */
24546             this.ft = Ext.get(this.bwrap.dom.lastChild);
24547             this.mc = Ext.get(mc);
24548         }else{
24549             this.createElement('header', d);
24550             this.createElement('bwrap', d);
24551
24552             // append the mid and bottom frame to the bwrap
24553             bw = this.bwrap.dom;
24554             this.createElement('tbar', bw);
24555             this.createElement('body', bw);
24556             this.createElement('bbar', bw);
24557             this.createElement('footer', bw);
24558
24559             if(!this.header){
24560                 this.body.addClass(this.bodyCls + '-noheader');
24561                 if(this.tbar){
24562                     this.tbar.addClass(this.tbarCls + '-noheader');
24563                 }
24564             }
24565         }
24566
24567         if(Ext.isDefined(this.padding)){
24568             this.body.setStyle('padding', this.body.addUnits(this.padding));
24569         }
24570
24571         if(this.border === false){
24572             this.el.addClass(this.baseCls + '-noborder');
24573             this.body.addClass(this.bodyCls + '-noborder');
24574             if(this.header){
24575                 this.header.addClass(this.headerCls + '-noborder');
24576             }
24577             if(this.footer){
24578                 this.footer.addClass(this.footerCls + '-noborder');
24579             }
24580             if(this.tbar){
24581                 this.tbar.addClass(this.tbarCls + '-noborder');
24582             }
24583             if(this.bbar){
24584                 this.bbar.addClass(this.bbarCls + '-noborder');
24585             }
24586         }
24587
24588         if(this.bodyBorder === false){
24589            this.body.addClass(this.bodyCls + '-noborder');
24590         }
24591
24592         this.bwrap.enableDisplayMode('block');
24593
24594         if(this.header){
24595             this.header.unselectable();
24596
24597             // for tools, we need to wrap any existing header markup
24598             if(this.headerAsText){
24599                 this.header.dom.innerHTML =
24600                     '<span class="' + this.headerTextCls + '">'+this.header.dom.innerHTML+'</span>';
24601
24602                 if(this.iconCls){
24603                     this.setIconClass(this.iconCls);
24604                 }
24605             }
24606         }
24607
24608         if(this.floating){
24609             this.makeFloating(this.floating);
24610         }
24611
24612         if(this.collapsible && this.titleCollapse && this.header){
24613             this.mon(this.header, 'click', this.toggleCollapse, this);
24614             this.header.setStyle('cursor', 'pointer');
24615         }
24616         if(ts){
24617             this.addTool.apply(this, ts);
24618         }
24619
24620         // Render Toolbars.
24621         if(this.fbar){
24622             this.footer.addClass('x-panel-btns');
24623             this.fbar.ownerCt = this;
24624             this.fbar.render(this.footer);
24625             this.footer.createChild({cls:'x-clear'});
24626         }
24627         if(this.tbar && this.topToolbar){
24628             this.topToolbar.ownerCt = this;
24629             this.topToolbar.render(this.tbar);
24630         }
24631         if(this.bbar && this.bottomToolbar){
24632             this.bottomToolbar.ownerCt = this;
24633             this.bottomToolbar.render(this.bbar);
24634         }
24635     },
24636
24637     /**
24638      * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
24639      * icon class if one has already been set and fire the {@link #iconchange} event after completion.
24640      * @param {String} cls The new CSS class name
24641      */
24642     setIconClass : function(cls){
24643         var old = this.iconCls;
24644         this.iconCls = cls;
24645         if(this.rendered && this.header){
24646             if(this.frame){
24647                 this.header.addClass('x-panel-icon');
24648                 this.header.replaceClass(old, this.iconCls);
24649             }else{
24650                 var hd = this.header,
24651                     img = hd.child('img.x-panel-inline-icon');
24652                 if(img){
24653                     Ext.fly(img).replaceClass(old, this.iconCls);
24654                 }else{
24655                     var hdspan = hd.child('span.' + this.headerTextCls);
24656                     if (hdspan) {
24657                         Ext.DomHelper.insertBefore(hdspan.dom, {
24658                             tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
24659                         });
24660                     }
24661                  }
24662             }
24663         }
24664         this.fireEvent('iconchange', this, cls, old);
24665     },
24666
24667     // private
24668     makeFloating : function(cfg){
24669         this.floating = true;
24670         this.el = new Ext.Layer(Ext.apply({}, cfg, {
24671             shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
24672             shadowOffset: this.shadowOffset,
24673             constrain:false,
24674             shim: this.shim === false ? false : undefined
24675         }), this.el);
24676     },
24677
24678     /**
24679      * Returns the {@link Ext.Toolbar toolbar} from the top (<code>{@link #tbar}</code>) section of the panel.
24680      * @return {Ext.Toolbar} The toolbar
24681      */
24682     getTopToolbar : function(){
24683         return this.topToolbar;
24684     },
24685
24686     /**
24687      * Returns the {@link Ext.Toolbar toolbar} from the bottom (<code>{@link #bbar}</code>) section of the panel.
24688      * @return {Ext.Toolbar} The toolbar
24689      */
24690     getBottomToolbar : function(){
24691         return this.bottomToolbar;
24692     },
24693
24694     /**
24695      * Returns the {@link Ext.Toolbar toolbar} from the footer (<code>{@link #fbar}</code>) section of the panel.
24696      * @return {Ext.Toolbar} The toolbar
24697      */
24698     getFooterToolbar : function() {
24699         return this.fbar;
24700     },
24701
24702     /**
24703      * Adds a button to this panel.  Note that this method must be called prior to rendering.  The preferred
24704      * approach is to add buttons via the {@link #buttons} config.
24705      * @param {String/Object} config A valid {@link Ext.Button} config.  A string will become the text for a default
24706      * button config, an object will be treated as a button config object.
24707      * @param {Function} handler The function to be called on button {@link Ext.Button#click}
24708      * @param {Object} scope The scope (<code>this</code> reference) in which the button handler function is executed. Defaults to the Button.
24709      * @return {Ext.Button} The button that was added
24710      */
24711     addButton : function(config, handler, scope){
24712         if(!this.fbar){
24713             this.createFbar([]);
24714         }
24715         if(handler){
24716             if(Ext.isString(config)){
24717                 config = {text: config};
24718             }
24719             config = Ext.apply({
24720                 handler: handler,
24721                 scope: scope
24722             }, config);
24723         }
24724         return this.fbar.add(config);
24725     },
24726
24727     // private
24728     addTool : function(){
24729         if(!this.rendered){
24730             if(!this.tools){
24731                 this.tools = [];
24732             }
24733             Ext.each(arguments, function(arg){
24734                 this.tools.push(arg);
24735             }, this);
24736             return;
24737         }
24738          // nowhere to render tools!
24739         if(!this[this.toolTarget]){
24740             return;
24741         }
24742         if(!this.toolTemplate){
24743             // initialize the global tool template on first use
24744             var tt = new Ext.Template(
24745                  '<div class="x-tool x-tool-{id}">&#160;</div>'
24746             );
24747             tt.disableFormats = true;
24748             tt.compile();
24749             Ext.Panel.prototype.toolTemplate = tt;
24750         }
24751         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
24752             var tc = a[i];
24753             if(!this.tools[tc.id]){
24754                 var overCls = 'x-tool-'+tc.id+'-over';
24755                 var t = this.toolTemplate.insertFirst(this[this.toolTarget], tc, true);
24756                 this.tools[tc.id] = t;
24757                 t.enableDisplayMode('block');
24758                 this.mon(t, 'click',  this.createToolHandler(t, tc, overCls, this));
24759                 if(tc.on){
24760                     this.mon(t, tc.on);
24761                 }
24762                 if(tc.hidden){
24763                     t.hide();
24764                 }
24765                 if(tc.qtip){
24766                     if(Ext.isObject(tc.qtip)){
24767                         Ext.QuickTips.register(Ext.apply({
24768                               target: t.id
24769                         }, tc.qtip));
24770                     } else {
24771                         t.dom.qtip = tc.qtip;
24772                     }
24773                 }
24774                 t.addClassOnOver(overCls);
24775             }
24776         }
24777     },
24778
24779     onLayout : function(shallow, force){
24780         Ext.Panel.superclass.onLayout.apply(this, arguments);
24781         if(this.hasLayout && this.toolbars.length > 0){
24782             Ext.each(this.toolbars, function(tb){
24783                 tb.doLayout(undefined, force);
24784             });
24785             this.syncHeight();
24786         }
24787     },
24788
24789     syncHeight : function(){
24790         var h = this.toolbarHeight,
24791                 bd = this.body,
24792                 lsh = this.lastSize.height,
24793                 sz;
24794
24795         if(this.autoHeight || !Ext.isDefined(lsh) || lsh == 'auto'){
24796             return;
24797         }
24798
24799
24800         if(h != this.getToolbarHeight()){
24801             h = Math.max(0, lsh - this.getFrameHeight());
24802             bd.setHeight(h);
24803             sz = bd.getSize();
24804             this.toolbarHeight = this.getToolbarHeight();
24805             this.onBodyResize(sz.width, sz.height);
24806         }
24807     },
24808
24809     // private
24810     onShow : function(){
24811         if(this.floating){
24812             return this.el.show();
24813         }
24814         Ext.Panel.superclass.onShow.call(this);
24815     },
24816
24817     // private
24818     onHide : function(){
24819         if(this.floating){
24820             return this.el.hide();
24821         }
24822         Ext.Panel.superclass.onHide.call(this);
24823     },
24824
24825     // private
24826     createToolHandler : function(t, tc, overCls, panel){
24827         return function(e){
24828             t.removeClass(overCls);
24829             if(tc.stopEvent !== false){
24830                 e.stopEvent();
24831             }
24832             if(tc.handler){
24833                 tc.handler.call(tc.scope || t, e, t, panel, tc);
24834             }
24835         };
24836     },
24837
24838     // private
24839     afterRender : function(){
24840         if(this.floating && !this.hidden){
24841             this.el.show();
24842         }
24843         if(this.title){
24844             this.setTitle(this.title);
24845         }
24846         Ext.Panel.superclass.afterRender.call(this); // do sizing calcs last
24847         if (this.collapsed) {
24848             this.collapsed = false;
24849             this.collapse(false);
24850         }
24851         this.initEvents();
24852     },
24853
24854     // private
24855     getKeyMap : function(){
24856         if(!this.keyMap){
24857             this.keyMap = new Ext.KeyMap(this.el, this.keys);
24858         }
24859         return this.keyMap;
24860     },
24861
24862     // private
24863     initEvents : function(){
24864         if(this.keys){
24865             this.getKeyMap();
24866         }
24867         if(this.draggable){
24868             this.initDraggable();
24869         }
24870         if(this.toolbars.length > 0){
24871             Ext.each(this.toolbars, function(tb){
24872                 tb.doLayout();
24873                 tb.on({
24874                     scope: this,
24875                     afterlayout: this.syncHeight,
24876                     remove: this.syncHeight
24877                 });
24878             }, this);
24879             this.syncHeight();
24880         }
24881
24882     },
24883
24884     // private
24885     initDraggable : function(){
24886         /**
24887          * <p>If this Panel is configured {@link #draggable}, this property will contain
24888          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
24889          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
24890          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
24891          * @type Ext.dd.DragSource.
24892          * @property dd
24893          */
24894         this.dd = new Ext.Panel.DD(this, Ext.isBoolean(this.draggable) ? null : this.draggable);
24895     },
24896
24897     // private
24898     beforeEffect : function(anim){
24899         if(this.floating){
24900             this.el.beforeAction();
24901         }
24902         if(anim !== false){
24903             this.el.addClass('x-panel-animated');
24904         }
24905     },
24906
24907     // private
24908     afterEffect : function(anim){
24909         this.syncShadow();
24910         this.el.removeClass('x-panel-animated');
24911     },
24912
24913     // private - wraps up an animation param with internal callbacks
24914     createEffect : function(a, cb, scope){
24915         var o = {
24916             scope:scope,
24917             block:true
24918         };
24919         if(a === true){
24920             o.callback = cb;
24921             return o;
24922         }else if(!a.callback){
24923             o.callback = cb;
24924         }else { // wrap it up
24925             o.callback = function(){
24926                 cb.call(scope);
24927                 Ext.callback(a.callback, a.scope);
24928             };
24929         }
24930         return Ext.applyIf(o, a);
24931     },
24932
24933     /**
24934      * Collapses the panel body so that it becomes hidden.  Fires the {@link #beforecollapse} event which will
24935      * cancel the collapse action if it returns false.
24936      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24937      * {@link #animCollapse} panel config)
24938      * @return {Ext.Panel} this
24939      */
24940     collapse : function(animate){
24941         if(this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforecollapse', this, animate) === false){
24942             return;
24943         }
24944         var doAnim = animate === true || (animate !== false && this.animCollapse);
24945         this.beforeEffect(doAnim);
24946         this.onCollapse(doAnim, animate);
24947         return this;
24948     },
24949
24950     // private
24951     onCollapse : function(doAnim, animArg){
24952         if(doAnim){
24953             this[this.collapseEl].slideOut(this.slideAnchor,
24954                     Ext.apply(this.createEffect(animArg||true, this.afterCollapse, this),
24955                         this.collapseDefaults));
24956         }else{
24957             this[this.collapseEl].hide(this.hideMode);
24958             this.afterCollapse(false);
24959         }
24960     },
24961
24962     // private
24963     afterCollapse : function(anim){
24964         this.collapsed = true;
24965         this.el.addClass(this.collapsedCls);
24966         if(anim !== false){
24967             this[this.collapseEl].hide(this.hideMode);
24968         }
24969         this.afterEffect(anim);
24970
24971         // Reset lastSize of all sub-components so they KNOW they are in a collapsed container
24972         this.cascade(function(c) {
24973             if (c.lastSize) {
24974                 c.lastSize = { width: undefined, height: undefined };
24975             }
24976         });
24977         this.fireEvent('collapse', this);
24978     },
24979
24980     /**
24981      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
24982      * cancel the expand action if it returns false.
24983      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
24984      * {@link #animCollapse} panel config)
24985      * @return {Ext.Panel} this
24986      */
24987     expand : function(animate){
24988         if(!this.collapsed || this.el.hasFxBlock() || this.fireEvent('beforeexpand', this, animate) === false){
24989             return;
24990         }
24991         var doAnim = animate === true || (animate !== false && this.animCollapse);
24992         this.el.removeClass(this.collapsedCls);
24993         this.beforeEffect(doAnim);
24994         this.onExpand(doAnim, animate);
24995         return this;
24996     },
24997
24998     // private
24999     onExpand : function(doAnim, animArg){
25000         if(doAnim){
25001             this[this.collapseEl].slideIn(this.slideAnchor,
25002                     Ext.apply(this.createEffect(animArg||true, this.afterExpand, this),
25003                         this.expandDefaults));
25004         }else{
25005             this[this.collapseEl].show(this.hideMode);
25006             this.afterExpand(false);
25007         }
25008     },
25009
25010     // private
25011     afterExpand : function(anim){
25012         this.collapsed = false;
25013         if(anim !== false){
25014             this[this.collapseEl].show(this.hideMode);
25015         }
25016         this.afterEffect(anim);
25017         if (this.deferLayout) {
25018             delete this.deferLayout;
25019             this.doLayout(true);
25020         }
25021         this.fireEvent('expand', this);
25022     },
25023
25024     /**
25025      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
25026      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
25027      * {@link #animCollapse} panel config)
25028      * @return {Ext.Panel} this
25029      */
25030     toggleCollapse : function(animate){
25031         this[this.collapsed ? 'expand' : 'collapse'](animate);
25032         return this;
25033     },
25034
25035     // private
25036     onDisable : function(){
25037         if(this.rendered && this.maskDisabled){
25038             this.el.mask();
25039         }
25040         Ext.Panel.superclass.onDisable.call(this);
25041     },
25042
25043     // private
25044     onEnable : function(){
25045         if(this.rendered && this.maskDisabled){
25046             this.el.unmask();
25047         }
25048         Ext.Panel.superclass.onEnable.call(this);
25049     },
25050
25051     // private
25052     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
25053         var w = adjWidth,
25054             h = adjHeight;
25055
25056         if(Ext.isDefined(w) || Ext.isDefined(h)){
25057             if(!this.collapsed){
25058                 // First, set the the Panel's body width.
25059                 // If we have auto-widthed it, get the resulting full offset width so we can size the Toolbars to match
25060                 // The Toolbars must not buffer this resize operation because we need to know their heights.
25061
25062                 if(Ext.isNumber(w)){
25063                     this.body.setWidth(w = this.adjustBodyWidth(w - this.getFrameWidth()));
25064                 } else if (w == 'auto') {
25065                     w = this.body.setWidth('auto').dom.offsetWidth;
25066                 } else {
25067                     w = this.body.dom.offsetWidth;
25068                 }
25069
25070                 if(this.tbar){
25071                     this.tbar.setWidth(w);
25072                     if(this.topToolbar){
25073                         this.topToolbar.setSize(w);
25074                     }
25075                 }
25076                 if(this.bbar){
25077                     this.bbar.setWidth(w);
25078                     if(this.bottomToolbar){
25079                         this.bottomToolbar.setSize(w);
25080                         // The bbar does not move on resize without this.
25081                         if (Ext.isIE) {
25082                             this.bbar.setStyle('position', 'static');
25083                             this.bbar.setStyle('position', '');
25084                         }
25085                     }
25086                 }
25087                 if(this.footer){
25088                     this.footer.setWidth(w);
25089                     if(this.fbar){
25090                         this.fbar.setSize(Ext.isIE ? (w - this.footer.getFrameWidth('lr')) : 'auto');
25091                     }
25092                 }
25093
25094                 // At this point, the Toolbars must be layed out for getFrameHeight to find a result.
25095                 if(Ext.isNumber(h)){
25096                     h = Math.max(0, h - this.getFrameHeight());
25097                     //h = Math.max(0, h - (this.getHeight() - this.body.getHeight()));
25098                     this.body.setHeight(h);
25099                 }else if(h == 'auto'){
25100                     this.body.setHeight(h);
25101                 }
25102
25103                 if(this.disabled && this.el._mask){
25104                     this.el._mask.setSize(this.el.dom.clientWidth, this.el.getHeight());
25105                 }
25106             }else{
25107                 // Adds an event to set the correct height afterExpand.  This accounts for the deferHeight flag in panel
25108                 this.queuedBodySize = {width: w, height: h};
25109                 if(!this.queuedExpand && this.allowQueuedExpand !== false){
25110                     this.queuedExpand = true;
25111                     this.on('expand', function(){
25112                         delete this.queuedExpand;
25113                         this.onResize(this.queuedBodySize.width, this.queuedBodySize.height);
25114                     }, this, {single:true});
25115                 }
25116             }
25117             this.onBodyResize(w, h);
25118         }
25119         this.syncShadow();
25120         Ext.Panel.superclass.onResize.call(this, adjWidth, adjHeight, rawWidth, rawHeight);
25121
25122     },
25123
25124     // private
25125     onBodyResize: function(w, h){
25126         this.fireEvent('bodyresize', this, w, h);
25127     },
25128
25129     // private
25130     getToolbarHeight: function(){
25131         var h = 0;
25132         if(this.rendered){
25133             Ext.each(this.toolbars, function(tb){
25134                 h += tb.getHeight();
25135             }, this);
25136         }
25137         return h;
25138     },
25139
25140     // deprecate
25141     adjustBodyHeight : function(h){
25142         return h;
25143     },
25144
25145     // private
25146     adjustBodyWidth : function(w){
25147         return w;
25148     },
25149
25150     // private
25151     onPosition : function(){
25152         this.syncShadow();
25153     },
25154
25155     /**
25156      * Returns the width in pixels of the framing elements of this panel (not including the body width).  To
25157      * retrieve the body width see {@link #getInnerWidth}.
25158      * @return {Number} The frame width
25159      */
25160     getFrameWidth : function(){
25161         var w = this.el.getFrameWidth('lr') + this.bwrap.getFrameWidth('lr');
25162
25163         if(this.frame){
25164             var l = this.bwrap.dom.firstChild;
25165             w += (Ext.fly(l).getFrameWidth('l') + Ext.fly(l.firstChild).getFrameWidth('r'));
25166             w += this.mc.getFrameWidth('lr');
25167         }
25168         return w;
25169     },
25170
25171     /**
25172      * Returns the height in pixels of the framing elements of this panel (including any top and bottom bars and
25173      * header and footer elements, but not including the body height).  To retrieve the body height see {@link #getInnerHeight}.
25174      * @return {Number} The frame height
25175      */
25176     getFrameHeight : function() {
25177         var h = Math.max(0, this.getHeight() - this.body.getHeight());
25178
25179         if (isNaN(h)) {
25180             h = 0;
25181         }
25182         return h;
25183
25184         /* Deprecate
25185             var h  = this.el.getFrameWidth('tb') + this.bwrap.getFrameWidth('tb');
25186             h += (this.tbar ? this.tbar.getHeight() : 0) +
25187                  (this.bbar ? this.bbar.getHeight() : 0);
25188
25189             if(this.frame){
25190                 h += this.el.dom.firstChild.offsetHeight + this.ft.dom.offsetHeight + this.mc.getFrameWidth('tb');
25191             }else{
25192                 h += (this.header ? this.header.getHeight() : 0) +
25193                     (this.footer ? this.footer.getHeight() : 0);
25194             }
25195             return h;
25196         */
25197     },
25198
25199     /**
25200      * Returns the width in pixels of the body element (not including the width of any framing elements).
25201      * For the frame width see {@link #getFrameWidth}.
25202      * @return {Number} The body width
25203      */
25204     getInnerWidth : function(){
25205         return this.getSize().width - this.getFrameWidth();
25206     },
25207
25208     /**
25209      * Returns the height in pixels of the body element (not including the height of any framing elements).
25210      * For the frame height see {@link #getFrameHeight}.
25211      * @return {Number} The body height
25212      */
25213     getInnerHeight : function(){
25214         return this.body.getHeight();
25215         /* Deprecate
25216             return this.getSize().height - this.getFrameHeight();
25217         */
25218     },
25219
25220     // private
25221     syncShadow : function(){
25222         if(this.floating){
25223             this.el.sync(true);
25224         }
25225     },
25226
25227     // private
25228     getLayoutTarget : function(){
25229         return this.body;
25230     },
25231
25232     // private
25233     getContentTarget : function(){
25234         return this.body;
25235     },
25236
25237     /**
25238      * <p>Sets the title text for the panel and optionally the {@link #iconCls icon class}.</p>
25239      * <p>In order to be able to set the title, a header element must have been created
25240      * for the Panel. This is triggered either by configuring the Panel with a non-blank <code>{@link #title}</code>,
25241      * or configuring it with <code><b>{@link #header}: true</b></code>.</p>
25242      * @param {String} title The title text to set
25243      * @param {String} iconCls (optional) {@link #iconCls iconCls} A user-defined CSS class that provides the icon image for this panel
25244      */
25245     setTitle : function(title, iconCls){
25246         this.title = title;
25247         if(this.header && this.headerAsText){
25248             this.header.child('span').update(title);
25249         }
25250         if(iconCls){
25251             this.setIconClass(iconCls);
25252         }
25253         this.fireEvent('titlechange', this, title);
25254         return this;
25255     },
25256
25257     /**
25258      * Get the {@link Ext.Updater} for this panel. Enables you to perform Ajax updates of this panel's body.
25259      * @return {Ext.Updater} The Updater
25260      */
25261     getUpdater : function(){
25262         return this.body.getUpdater();
25263     },
25264
25265      /**
25266      * Loads this content panel immediately with content returned from an XHR call.
25267      * @param {Object/String/Function} config A config object containing any of the following options:
25268 <pre><code>
25269 panel.load({
25270     url: 'your-url.php',
25271     params: {param1: 'foo', param2: 'bar'}, // or a URL encoded string
25272     callback: yourFunction,
25273     scope: yourObject, // optional scope for the callback
25274     discardUrl: false,
25275     nocache: false,
25276     text: 'Loading...',
25277     timeout: 30,
25278     scripts: false
25279 });
25280 </code></pre>
25281      * The only required property is url. The optional properties nocache, text and scripts
25282      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their
25283      * associated property on this panel Updater instance.
25284      * @return {Ext.Panel} this
25285      */
25286     load : function(){
25287         var um = this.body.getUpdater();
25288         um.update.apply(um, arguments);
25289         return this;
25290     },
25291
25292     // private
25293     beforeDestroy : function(){
25294         Ext.Panel.superclass.beforeDestroy.call(this);
25295         if(this.header){
25296             this.header.removeAllListeners();
25297         }
25298         if(this.tools){
25299             for(var k in this.tools){
25300                 Ext.destroy(this.tools[k]);
25301             }
25302         }
25303         if(this.toolbars.length > 0){
25304             Ext.each(this.toolbars, function(tb){
25305                 tb.un('afterlayout', this.syncHeight, this);
25306                 tb.un('remove', this.syncHeight, this);
25307             }, this);
25308         }
25309         if(Ext.isArray(this.buttons)){
25310             while(this.buttons.length) {
25311                 Ext.destroy(this.buttons[0]);
25312             }
25313         }
25314         if(this.rendered){
25315             Ext.destroy(
25316                 this.ft,
25317                 this.header,
25318                 this.footer,
25319                 this.tbar,
25320                 this.bbar,
25321                 this.body,
25322                 this.mc,
25323                 this.bwrap,
25324                 this.dd
25325             );
25326             if (this.fbar) {
25327                 Ext.destroy(
25328                     this.fbar,
25329                     this.fbar.el
25330                 );
25331             }
25332         }
25333         Ext.destroy(this.toolbars);
25334     },
25335
25336     // private
25337     createClasses : function(){
25338         this.headerCls = this.baseCls + '-header';
25339         this.headerTextCls = this.baseCls + '-header-text';
25340         this.bwrapCls = this.baseCls + '-bwrap';
25341         this.tbarCls = this.baseCls + '-tbar';
25342         this.bodyCls = this.baseCls + '-body';
25343         this.bbarCls = this.baseCls + '-bbar';
25344         this.footerCls = this.baseCls + '-footer';
25345     },
25346
25347     // private
25348     createGhost : function(cls, useShim, appendTo){
25349         var el = document.createElement('div');
25350         el.className = 'x-panel-ghost ' + (cls ? cls : '');
25351         if(this.header){
25352             el.appendChild(this.el.dom.firstChild.cloneNode(true));
25353         }
25354         Ext.fly(el.appendChild(document.createElement('ul'))).setHeight(this.bwrap.getHeight());
25355         el.style.width = this.el.dom.offsetWidth + 'px';;
25356         if(!appendTo){
25357             this.container.dom.appendChild(el);
25358         }else{
25359             Ext.getDom(appendTo).appendChild(el);
25360         }
25361         if(useShim !== false && this.el.useShim !== false){
25362             var layer = new Ext.Layer({shadow:false, useDisplay:true, constrain:false}, el);
25363             layer.show();
25364             return layer;
25365         }else{
25366             return new Ext.Element(el);
25367         }
25368     },
25369
25370     // private
25371     doAutoLoad : function(){
25372         var u = this.body.getUpdater();
25373         if(this.renderer){
25374             u.setRenderer(this.renderer);
25375         }
25376         u.update(Ext.isObject(this.autoLoad) ? this.autoLoad : {url: this.autoLoad});
25377     },
25378
25379     /**
25380      * Retrieve a tool by id.
25381      * @param {String} id
25382      * @return {Object} tool
25383      */
25384     getTool : function(id) {
25385         return this.tools[id];
25386     }
25387
25388 /**
25389  * @cfg {String} autoEl @hide
25390  */
25391 });
25392 Ext.reg('panel', Ext.Panel);
25393 /**
25394  * @class Ext.Editor
25395  * @extends Ext.Component
25396  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
25397  * @constructor
25398  * Create a new Editor
25399  * @param {Object} config The config object
25400  * @xtype editor
25401  */
25402 Ext.Editor = function(field, config){
25403     if(field.field){
25404         this.field = Ext.create(field.field, 'textfield');
25405         config = Ext.apply({}, field); // copy so we don't disturb original config
25406         delete config.field;
25407     }else{
25408         this.field = field;
25409     }
25410     Ext.Editor.superclass.constructor.call(this, config);
25411 };
25412
25413 Ext.extend(Ext.Editor, Ext.Component, {
25414     /**
25415     * @cfg {Ext.form.Field} field
25416     * The Field object (or descendant) or config object for field
25417     */
25418     /**
25419      * @cfg {Boolean} allowBlur
25420      * True to {@link #completeEdit complete the editing process} if in edit mode when the
25421      * field is blurred. Defaults to <tt>true</tt>.
25422      */
25423     allowBlur: true,
25424     /**
25425      * @cfg {Boolean/String} autoSize
25426      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
25427      * or "height" to adopt the height only, "none" to always use the field dimensions. (defaults to false)
25428      */
25429     /**
25430      * @cfg {Boolean} revertInvalid
25431      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
25432      * validation fails (defaults to true)
25433      */
25434     /**
25435      * @cfg {Boolean} ignoreNoChange
25436      * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
25437      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
25438      * will never be ignored.
25439      */
25440     /**
25441      * @cfg {Boolean} hideEl
25442      * False to keep the bound element visible while the editor is displayed (defaults to true)
25443      */
25444     /**
25445      * @cfg {Mixed} value
25446      * The data value of the underlying field (defaults to "")
25447      */
25448     value : "",
25449     /**
25450      * @cfg {String} alignment
25451      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "c-c?").
25452      */
25453     alignment: "c-c?",
25454     /**
25455      * @cfg {Array} offsets
25456      * The offsets to use when aligning (see {@link Ext.Element#alignTo} for more details. Defaults to <tt>[0, 0]</tt>.
25457      */
25458     offsets: [0, 0],
25459     /**
25460      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
25461      * for bottom-right shadow (defaults to "frame")
25462      */
25463     shadow : "frame",
25464     /**
25465      * @cfg {Boolean} constrain True to constrain the editor to the viewport
25466      */
25467     constrain : false,
25468     /**
25469      * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
25470      */
25471     swallowKeys : true,
25472     /**
25473      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
25474      */
25475     completeOnEnter : true,
25476     /**
25477      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
25478      */
25479     cancelOnEsc : true,
25480     /**
25481      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
25482      */
25483     updateEl : false,
25484
25485     initComponent : function(){
25486         Ext.Editor.superclass.initComponent.call(this);
25487         this.addEvents(
25488             /**
25489              * @event beforestartedit
25490              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
25491              * false from the handler of this event.
25492              * @param {Editor} this
25493              * @param {Ext.Element} boundEl The underlying element bound to this editor
25494              * @param {Mixed} value The field value being set
25495              */
25496             "beforestartedit",
25497             /**
25498              * @event startedit
25499              * Fires when this editor is displayed
25500              * @param {Ext.Element} boundEl The underlying element bound to this editor
25501              * @param {Mixed} value The starting field value
25502              */
25503             "startedit",
25504             /**
25505              * @event beforecomplete
25506              * Fires after a change has been made to the field, but before the change is reflected in the underlying
25507              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
25508              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
25509              * event will not fire since no edit actually occurred.
25510              * @param {Editor} this
25511              * @param {Mixed} value The current field value
25512              * @param {Mixed} startValue The original field value
25513              */
25514             "beforecomplete",
25515             /**
25516              * @event complete
25517              * Fires after editing is complete and any changed value has been written to the underlying field.
25518              * @param {Editor} this
25519              * @param {Mixed} value The current field value
25520              * @param {Mixed} startValue The original field value
25521              */
25522             "complete",
25523             /**
25524              * @event canceledit
25525              * Fires after editing has been canceled and the editor's value has been reset.
25526              * @param {Editor} this
25527              * @param {Mixed} value The user-entered field value that was discarded
25528              * @param {Mixed} startValue The original field value that was set back into the editor after cancel
25529              */
25530             "canceledit",
25531             /**
25532              * @event specialkey
25533              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
25534              * {@link Ext.EventObject#getKey} to determine which key was pressed.
25535              * @param {Ext.form.Field} this
25536              * @param {Ext.EventObject} e The event object
25537              */
25538             "specialkey"
25539         );
25540     },
25541
25542     // private
25543     onRender : function(ct, position){
25544         this.el = new Ext.Layer({
25545             shadow: this.shadow,
25546             cls: "x-editor",
25547             parentEl : ct,
25548             shim : this.shim,
25549             shadowOffset: this.shadowOffset || 4,
25550             id: this.id,
25551             constrain: this.constrain
25552         });
25553         if(this.zIndex){
25554             this.el.setZIndex(this.zIndex);
25555         }
25556         this.el.setStyle("overflow", Ext.isGecko ? "auto" : "hidden");
25557         if(this.field.msgTarget != 'title'){
25558             this.field.msgTarget = 'qtip';
25559         }
25560         this.field.inEditor = true;
25561         this.mon(this.field, {
25562             scope: this,
25563             blur: this.onBlur,
25564             specialkey: this.onSpecialKey
25565         });
25566         if(this.field.grow){
25567             this.mon(this.field, "autosize", this.el.sync,  this.el, {delay:1});
25568         }
25569         this.field.render(this.el).show();
25570         this.field.getEl().dom.name = '';
25571         if(this.swallowKeys){
25572             this.field.el.swallowEvent([
25573                 'keypress', // *** Opera
25574                 'keydown'   // *** all other browsers
25575             ]);
25576         }
25577     },
25578
25579     // private
25580     onSpecialKey : function(field, e){
25581         var key = e.getKey(),
25582             complete = this.completeOnEnter && key == e.ENTER,
25583             cancel = this.cancelOnEsc && key == e.ESC;
25584         if(complete || cancel){
25585             e.stopEvent();
25586             if(complete){
25587                 this.completeEdit();
25588             }else{
25589                 this.cancelEdit();
25590             }
25591             if(field.triggerBlur){
25592                 field.triggerBlur();
25593             }
25594         }
25595         this.fireEvent('specialkey', field, e);
25596     },
25597
25598     /**
25599      * Starts the editing process and shows the editor.
25600      * @param {Mixed} el The element to edit
25601      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
25602       * to the innerHTML of el.
25603      */
25604     startEdit : function(el, value){
25605         if(this.editing){
25606             this.completeEdit();
25607         }
25608         this.boundEl = Ext.get(el);
25609         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
25610         if(!this.rendered){
25611             this.render(this.parentEl || document.body);
25612         }
25613         if(this.fireEvent("beforestartedit", this, this.boundEl, v) !== false){
25614             this.startValue = v;
25615             this.field.reset();
25616             this.field.setValue(v);
25617             this.realign(true);
25618             this.editing = true;
25619             this.show();
25620         }
25621     },
25622
25623     // private
25624     doAutoSize : function(){
25625         if(this.autoSize){
25626             var sz = this.boundEl.getSize(),
25627                 fs = this.field.getSize();
25628
25629             switch(this.autoSize){
25630                 case "width":
25631                     this.setSize(sz.width, fs.height);
25632                     break;
25633                 case "height":
25634                     this.setSize(fs.width, sz.height);
25635                     break;
25636                 case "none":
25637                     this.setSize(fs.width, fs.height);
25638                     break;
25639                 default:
25640                     this.setSize(sz.width, sz.height);
25641             }
25642         }
25643     },
25644
25645     /**
25646      * Sets the height and width of this editor.
25647      * @param {Number} width The new width
25648      * @param {Number} height The new height
25649      */
25650     setSize : function(w, h){
25651         delete this.field.lastSize;
25652         this.field.setSize(w, h);
25653         if(this.el){
25654             if(Ext.isGecko2 || Ext.isOpera){
25655                 // prevent layer scrollbars
25656                 this.el.setSize(w, h);
25657             }
25658             this.el.sync();
25659         }
25660     },
25661
25662     /**
25663      * Realigns the editor to the bound field based on the current alignment config value.
25664      * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
25665      */
25666     realign : function(autoSize){
25667         if(autoSize === true){
25668             this.doAutoSize();
25669         }
25670         this.el.alignTo(this.boundEl, this.alignment, this.offsets);
25671     },
25672
25673     /**
25674      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
25675      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
25676      */
25677     completeEdit : function(remainVisible){
25678         if(!this.editing){
25679             return;
25680         }
25681         // Assert combo values first
25682         if (this.field.assertValue) {
25683             this.field.assertValue();
25684         }
25685         var v = this.getValue();
25686         if(!this.field.isValid()){
25687             if(this.revertInvalid !== false){
25688                 this.cancelEdit(remainVisible);
25689             }
25690             return;
25691         }
25692         if(String(v) === String(this.startValue) && this.ignoreNoChange){
25693             this.hideEdit(remainVisible);
25694             return;
25695         }
25696         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
25697             v = this.getValue();
25698             if(this.updateEl && this.boundEl){
25699                 this.boundEl.update(v);
25700             }
25701             this.hideEdit(remainVisible);
25702             this.fireEvent("complete", this, v, this.startValue);
25703         }
25704     },
25705
25706     // private
25707     onShow : function(){
25708         this.el.show();
25709         if(this.hideEl !== false){
25710             this.boundEl.hide();
25711         }
25712         this.field.show().focus(false, true);
25713         this.fireEvent("startedit", this.boundEl, this.startValue);
25714     },
25715
25716     /**
25717      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
25718      * reverted to the original starting value.
25719      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
25720      * cancel (defaults to false)
25721      */
25722     cancelEdit : function(remainVisible){
25723         if(this.editing){
25724             var v = this.getValue();
25725             this.setValue(this.startValue);
25726             this.hideEdit(remainVisible);
25727             this.fireEvent("canceledit", this, v, this.startValue);
25728         }
25729     },
25730
25731     // private
25732     hideEdit: function(remainVisible){
25733         if(remainVisible !== true){
25734             this.editing = false;
25735             this.hide();
25736         }
25737     },
25738
25739     // private
25740     onBlur : function(){
25741         // selectSameEditor flag allows the same editor to be started without onBlur firing on itself
25742         if(this.allowBlur === true && this.editing && this.selectSameEditor !== true){
25743             this.completeEdit();
25744         }
25745     },
25746
25747     // private
25748     onHide : function(){
25749         if(this.editing){
25750             this.completeEdit();
25751             return;
25752         }
25753         this.field.blur();
25754         if(this.field.collapse){
25755             this.field.collapse();
25756         }
25757         this.el.hide();
25758         if(this.hideEl !== false){
25759             this.boundEl.show();
25760         }
25761     },
25762
25763     /**
25764      * Sets the data value of the editor
25765      * @param {Mixed} value Any valid value supported by the underlying field
25766      */
25767     setValue : function(v){
25768         this.field.setValue(v);
25769     },
25770
25771     /**
25772      * Gets the data value of the editor
25773      * @return {Mixed} The data value
25774      */
25775     getValue : function(){
25776         return this.field.getValue();
25777     },
25778
25779     beforeDestroy : function(){
25780         Ext.destroyMembers(this, 'field');
25781
25782         delete this.parentEl;
25783         delete this.boundEl;
25784     }
25785 });
25786 Ext.reg('editor', Ext.Editor);
25787 /**
25788  * @class Ext.ColorPalette
25789  * @extends Ext.Component
25790  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25791  * Here's an example of typical usage:
25792  * <pre><code>
25793 var cp = new Ext.ColorPalette({value:'993300'});  // initial selected color
25794 cp.render('my-div');
25795
25796 cp.on('select', function(palette, selColor){
25797     // do something with selColor
25798 });
25799 </code></pre>
25800  * @constructor
25801  * Create a new ColorPalette
25802  * @param {Object} config The config object
25803  * @xtype colorpalette
25804  */
25805 Ext.ColorPalette = Ext.extend(Ext.Component, {
25806         /**
25807          * @cfg {String} tpl An existing XTemplate instance to be used in place of the default template for rendering the component.
25808          */
25809     /**
25810      * @cfg {String} itemCls
25811      * The CSS class to apply to the containing element (defaults to 'x-color-palette')
25812      */
25813     itemCls : 'x-color-palette',
25814     /**
25815      * @cfg {String} value
25816      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25817      * the hex codes are case-sensitive.
25818      */
25819     value : null,
25820     /**
25821      * @cfg {String} clickEvent
25822      * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu). 
25823      * Defaults to <tt>'click'</tt>.
25824      */
25825     clickEvent :'click',
25826     // private
25827     ctype : 'Ext.ColorPalette',
25828
25829     /**
25830      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
25831      */
25832     allowReselect : false,
25833
25834     /**
25835      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25836      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25837      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25838      * of colors with the width setting until the box is symmetrical.</p>
25839      * <p>You can override individual colors if needed:</p>
25840      * <pre><code>
25841 var cp = new Ext.ColorPalette();
25842 cp.colors[0] = 'FF0000';  // change the first box to red
25843 </code></pre>
25844
25845 Or you can provide a custom array of your own for complete control:
25846 <pre><code>
25847 var cp = new Ext.ColorPalette();
25848 cp.colors = ['000000', '993300', '333300'];
25849 </code></pre>
25850      * @type Array
25851      */
25852     colors : [
25853         '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
25854         '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
25855         'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
25856         'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
25857         'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
25858     ],
25859
25860     /**
25861      * @cfg {Function} handler
25862      * Optional. A function that will handle the select event of this palette.
25863      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
25864      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
25865      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
25866      * </ul></div>
25867      */
25868     /**
25869      * @cfg {Object} scope
25870      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
25871      * function will be called.  Defaults to this ColorPalette instance.
25872      */
25873     
25874     // private
25875     initComponent : function(){
25876         Ext.ColorPalette.superclass.initComponent.call(this);
25877         this.addEvents(
25878             /**
25879              * @event select
25880              * Fires when a color is selected
25881              * @param {ColorPalette} this
25882              * @param {String} color The 6-digit color hex code (without the # symbol)
25883              */
25884             'select'
25885         );
25886
25887         if(this.handler){
25888             this.on('select', this.handler, this.scope, true);
25889         }    
25890     },
25891
25892     // private
25893     onRender : function(container, position){
25894         this.autoEl = {
25895             tag: 'div',
25896             cls: this.itemCls
25897         };
25898         Ext.ColorPalette.superclass.onRender.call(this, container, position);
25899         var t = this.tpl || new Ext.XTemplate(
25900             '<tpl for="."><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>'
25901         );
25902         t.overwrite(this.el, this.colors);
25903         this.mon(this.el, this.clickEvent, this.handleClick, this, {delegate: 'a'});
25904         if(this.clickEvent != 'click'){
25905                 this.mon(this.el, 'click', Ext.emptyFn, this, {delegate: 'a', preventDefault: true});
25906         }
25907     },
25908
25909     // private
25910     afterRender : function(){
25911         Ext.ColorPalette.superclass.afterRender.call(this);
25912         if(this.value){
25913             var s = this.value;
25914             this.value = null;
25915             this.select(s, true);
25916         }
25917     },
25918
25919     // private
25920     handleClick : function(e, t){
25921         e.preventDefault();
25922         if(!this.disabled){
25923             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25924             this.select(c.toUpperCase());
25925         }
25926     },
25927
25928     /**
25929      * Selects the specified color in the palette (fires the {@link #select} event)
25930      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25931      * @param {Boolean} suppressEvent (optional) True to stop the select event from firing. Defaults to <tt>false</tt>.
25932      */
25933     select : function(color, suppressEvent){
25934         color = color.replace('#', '');
25935         if(color != this.value || this.allowReselect){
25936             var el = this.el;
25937             if(this.value){
25938                 el.child('a.color-'+this.value).removeClass('x-color-palette-sel');
25939             }
25940             el.child('a.color-'+color).addClass('x-color-palette-sel');
25941             this.value = color;
25942             if(suppressEvent !== true){
25943                 this.fireEvent('select', this, color);
25944             }
25945         }
25946     }
25947
25948     /**
25949      * @cfg {String} autoEl @hide
25950      */
25951 });
25952 Ext.reg('colorpalette', Ext.ColorPalette);/**
25953  * @class Ext.DatePicker
25954  * @extends Ext.Component
25955  * <p>A popup date picker. This class is used by the {@link Ext.form.DateField DateField} class
25956  * to allow browsing and selection of valid dates.</p>
25957  * <p>All the string values documented below may be overridden by including an Ext locale file in
25958  * your page.</p>
25959  * @constructor
25960  * Create a new DatePicker
25961  * @param {Object} config The config object
25962  * @xtype datepicker
25963  */
25964 Ext.DatePicker = Ext.extend(Ext.BoxComponent, {
25965     /**
25966      * @cfg {String} todayText
25967      * The text to display on the button that selects the current date (defaults to <code>'Today'</code>)
25968      */
25969     todayText : 'Today',
25970     /**
25971      * @cfg {String} okText
25972      * The text to display on the ok button (defaults to <code>'&#160;OK&#160;'</code> to give the user extra clicking room)
25973      */
25974     okText : '&#160;OK&#160;',
25975     /**
25976      * @cfg {String} cancelText
25977      * The text to display on the cancel button (defaults to <code>'Cancel'</code>)
25978      */
25979     cancelText : 'Cancel',
25980     /**
25981      * @cfg {Function} handler
25982      * Optional. A function that will handle the select event of this picker.
25983      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
25984      * <li><code>picker</code> : DatePicker<div class="sub-desc">This DatePicker.</div></li>
25985      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
25986      * </ul></div>
25987      */
25988     /**
25989      * @cfg {Object} scope
25990      * The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
25991      * function will be called.  Defaults to this DatePicker instance.
25992      */
25993     /**
25994      * @cfg {String} todayTip
25995      * A string used to format the message for displaying in a tooltip over the button that
25996      * selects the current date. Defaults to <code>'{0} (Spacebar)'</code> where
25997      * the <code>{0}</code> token is replaced by today's date.
25998      */
25999     todayTip : '{0} (Spacebar)',
26000     /**
26001      * @cfg {String} minText
26002      * The error text to display if the minDate validation fails (defaults to <code>'This date is before the minimum date'</code>)
26003      */
26004     minText : 'This date is before the minimum date',
26005     /**
26006      * @cfg {String} maxText
26007      * The error text to display if the maxDate validation fails (defaults to <code>'This date is after the maximum date'</code>)
26008      */
26009     maxText : 'This date is after the maximum date',
26010     /**
26011      * @cfg {String} format
26012      * The default date format string which can be overriden for localization support.  The format must be
26013      * valid according to {@link Date#parseDate} (defaults to <code>'m/d/y'</code>).
26014      */
26015     format : 'm/d/y',
26016     /**
26017      * @cfg {String} disabledDaysText
26018      * The tooltip to display when the date falls on a disabled day (defaults to <code>'Disabled'</code>)
26019      */
26020     disabledDaysText : 'Disabled',
26021     /**
26022      * @cfg {String} disabledDatesText
26023      * The tooltip text to display when the date falls on a disabled date (defaults to <code>'Disabled'</code>)
26024      */
26025     disabledDatesText : 'Disabled',
26026     /**
26027      * @cfg {Array} monthNames
26028      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26029      */
26030     monthNames : Date.monthNames,
26031     /**
26032      * @cfg {Array} dayNames
26033      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26034      */
26035     dayNames : Date.dayNames,
26036     /**
26037      * @cfg {String} nextText
26038      * The next month navigation button tooltip (defaults to <code>'Next Month (Control+Right)'</code>)
26039      */
26040     nextText : 'Next Month (Control+Right)',
26041     /**
26042      * @cfg {String} prevText
26043      * The previous month navigation button tooltip (defaults to <code>'Previous Month (Control+Left)'</code>)
26044      */
26045     prevText : 'Previous Month (Control+Left)',
26046     /**
26047      * @cfg {String} monthYearText
26048      * The header month selector tooltip (defaults to <code>'Choose a month (Control+Up/Down to move years)'</code>)
26049      */
26050     monthYearText : 'Choose a month (Control+Up/Down to move years)',
26051     /**
26052      * @cfg {Number} startDay
26053      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26054      */
26055     startDay : 0,
26056     /**
26057      * @cfg {Boolean} showToday
26058      * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
26059      * that selects the current date (defaults to <code>true</code>).
26060      */
26061     showToday : true,
26062     /**
26063      * @cfg {Date} minDate
26064      * Minimum allowable date (JavaScript date object, defaults to null)
26065      */
26066     /**
26067      * @cfg {Date} maxDate
26068      * Maximum allowable date (JavaScript date object, defaults to null)
26069      */
26070     /**
26071      * @cfg {Array} disabledDays
26072      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26073      */
26074     /**
26075      * @cfg {RegExp} disabledDatesRE
26076      * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}
26077      * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
26078      * disabledDates value.
26079      */
26080     /**
26081      * @cfg {Array} disabledDates
26082      * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
26083      * expression so they are very powerful. Some examples:
26084      * <ul>
26085      * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>
26086      * <li>['03/08', '09/16'] would disable those days for every year</li>
26087      * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>
26088      * <li>['03/../2006'] would disable every day in March 2006</li>
26089      * <li>['^03'] would disable every day in every March</li>
26090      * </ul>
26091      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
26092      * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
26093      * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
26094      */
26095
26096     // private
26097     // Set by other components to stop the picker focus being updated when the value changes.
26098     focusOnSelect: true,
26099
26100     // default value used to initialise each date in the DatePicker
26101     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
26102     initHour: 12, // 24-hour format
26103
26104     // private
26105     initComponent : function(){
26106         Ext.DatePicker.superclass.initComponent.call(this);
26107
26108         this.value = this.value ?
26109                  this.value.clearTime(true) : new Date().clearTime();
26110
26111         this.addEvents(
26112             /**
26113              * @event select
26114              * Fires when a date is selected
26115              * @param {DatePicker} this DatePicker
26116              * @param {Date} date The selected date
26117              */
26118             'select'
26119         );
26120
26121         if(this.handler){
26122             this.on('select', this.handler,  this.scope || this);
26123         }
26124
26125         this.initDisabledDays();
26126     },
26127
26128     // private
26129     initDisabledDays : function(){
26130         if(!this.disabledDatesRE && this.disabledDates){
26131             var dd = this.disabledDates,
26132                 len = dd.length - 1,
26133                 re = '(?:';
26134
26135             Ext.each(dd, function(d, i){
26136                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
26137                 if(i != len){
26138                     re += '|';
26139                 }
26140             }, this);
26141             this.disabledDatesRE = new RegExp(re + ')');
26142         }
26143     },
26144
26145     /**
26146      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
26147      * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
26148      * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
26149      */
26150     setDisabledDates : function(dd){
26151         if(Ext.isArray(dd)){
26152             this.disabledDates = dd;
26153             this.disabledDatesRE = null;
26154         }else{
26155             this.disabledDatesRE = dd;
26156         }
26157         this.initDisabledDays();
26158         this.update(this.value, true);
26159     },
26160
26161     /**
26162      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
26163      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
26164      * for details on supported values.
26165      */
26166     setDisabledDays : function(dd){
26167         this.disabledDays = dd;
26168         this.update(this.value, true);
26169     },
26170
26171     /**
26172      * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
26173      * @param {Date} value The minimum date that can be selected
26174      */
26175     setMinDate : function(dt){
26176         this.minDate = dt;
26177         this.update(this.value, true);
26178     },
26179
26180     /**
26181      * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
26182      * @param {Date} value The maximum date that can be selected
26183      */
26184     setMaxDate : function(dt){
26185         this.maxDate = dt;
26186         this.update(this.value, true);
26187     },
26188
26189     /**
26190      * Sets the value of the date field
26191      * @param {Date} value The date to set
26192      */
26193     setValue : function(value){
26194         this.value = value.clearTime(true);
26195         this.update(this.value);
26196     },
26197
26198     /**
26199      * Gets the current selected value of the date field
26200      * @return {Date} The selected date
26201      */
26202     getValue : function(){
26203         return this.value;
26204     },
26205
26206     // private
26207     focus : function(){
26208         this.update(this.activeDate);
26209     },
26210
26211     // private
26212     onEnable: function(initial){
26213         Ext.DatePicker.superclass.onEnable.call(this);
26214         this.doDisabled(false);
26215         this.update(initial ? this.value : this.activeDate);
26216         if(Ext.isIE){
26217             this.el.repaint();
26218         }
26219
26220     },
26221
26222     // private
26223     onDisable : function(){
26224         Ext.DatePicker.superclass.onDisable.call(this);
26225         this.doDisabled(true);
26226         if(Ext.isIE && !Ext.isIE8){
26227             /* Really strange problem in IE6/7, when disabled, have to explicitly
26228              * repaint each of the nodes to get them to display correctly, simply
26229              * calling repaint on the main element doesn't appear to be enough.
26230              */
26231              Ext.each([].concat(this.textNodes, this.el.query('th span')), function(el){
26232                  Ext.fly(el).repaint();
26233              });
26234         }
26235     },
26236
26237     // private
26238     doDisabled : function(disabled){
26239         this.keyNav.setDisabled(disabled);
26240         this.prevRepeater.setDisabled(disabled);
26241         this.nextRepeater.setDisabled(disabled);
26242         if(this.showToday){
26243             this.todayKeyListener.setDisabled(disabled);
26244             this.todayBtn.setDisabled(disabled);
26245         }
26246     },
26247
26248     // private
26249     onRender : function(container, position){
26250         var m = [
26251              '<table cellspacing="0">',
26252                 '<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>',
26253                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'],
26254                 dn = this.dayNames,
26255                 i;
26256         for(i = 0; i < 7; i++){
26257             var d = this.startDay+i;
26258             if(d > 6){
26259                 d = d-7;
26260             }
26261             m.push('<th><span>', dn[d].substr(0,1), '</span></th>');
26262         }
26263         m[m.length] = '</tr></thead><tbody><tr>';
26264         for(i = 0; i < 42; i++) {
26265             if(i % 7 === 0 && i !== 0){
26266                 m[m.length] = '</tr><tr>';
26267             }
26268             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26269         }
26270         m.push('</tr></tbody></table></td></tr>',
26271                 this.showToday ? '<tr><td colspan="3" class="x-date-bottom" align="center"></td></tr>' : '',
26272                 '</table><div class="x-date-mp"></div>');
26273
26274         var el = document.createElement('div');
26275         el.className = 'x-date-picker';
26276         el.innerHTML = m.join('');
26277
26278         container.dom.insertBefore(el, position);
26279
26280         this.el = Ext.get(el);
26281         this.eventEl = Ext.get(el.firstChild);
26282
26283         this.prevRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-left a'), {
26284             handler: this.showPrevMonth,
26285             scope: this,
26286             preventDefault:true,
26287             stopDefault:true
26288         });
26289
26290         this.nextRepeater = new Ext.util.ClickRepeater(this.el.child('td.x-date-right a'), {
26291             handler: this.showNextMonth,
26292             scope: this,
26293             preventDefault:true,
26294             stopDefault:true
26295         });
26296
26297         this.monthPicker = this.el.down('div.x-date-mp');
26298         this.monthPicker.enableDisplayMode('block');
26299
26300         this.keyNav = new Ext.KeyNav(this.eventEl, {
26301             'left' : function(e){
26302                 if(e.ctrlKey){
26303                     this.showPrevMonth();
26304                 }else{
26305                     this.update(this.activeDate.add('d', -1));
26306                 }
26307             },
26308
26309             'right' : function(e){
26310                 if(e.ctrlKey){
26311                     this.showNextMonth();
26312                 }else{
26313                     this.update(this.activeDate.add('d', 1));
26314                 }
26315             },
26316
26317             'up' : function(e){
26318                 if(e.ctrlKey){
26319                     this.showNextYear();
26320                 }else{
26321                     this.update(this.activeDate.add('d', -7));
26322                 }
26323             },
26324
26325             'down' : function(e){
26326                 if(e.ctrlKey){
26327                     this.showPrevYear();
26328                 }else{
26329                     this.update(this.activeDate.add('d', 7));
26330                 }
26331             },
26332
26333             'pageUp' : function(e){
26334                 this.showNextMonth();
26335             },
26336
26337             'pageDown' : function(e){
26338                 this.showPrevMonth();
26339             },
26340
26341             'enter' : function(e){
26342                 e.stopPropagation();
26343                 return true;
26344             },
26345
26346             scope : this
26347         });
26348
26349         this.el.unselectable();
26350
26351         this.cells = this.el.select('table.x-date-inner tbody td');
26352         this.textNodes = this.el.query('table.x-date-inner tbody span');
26353
26354         this.mbtn = new Ext.Button({
26355             text: '&#160;',
26356             tooltip: this.monthYearText,
26357             renderTo: this.el.child('td.x-date-middle', true)
26358         });
26359         this.mbtn.el.child('em').addClass('x-btn-arrow');
26360
26361         if(this.showToday){
26362             this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
26363             var today = (new Date()).dateFormat(this.format);
26364             this.todayBtn = new Ext.Button({
26365                 renderTo: this.el.child('td.x-date-bottom', true),
26366                 text: String.format(this.todayText, today),
26367                 tooltip: String.format(this.todayTip, today),
26368                 handler: this.selectToday,
26369                 scope: this
26370             });
26371         }
26372         this.mon(this.eventEl, 'mousewheel', this.handleMouseWheel, this);
26373         this.mon(this.eventEl, 'click', this.handleDateClick,  this, {delegate: 'a.x-date-date'});
26374         this.mon(this.mbtn, 'click', this.showMonthPicker, this);
26375         this.onEnable(true);
26376     },
26377
26378     // private
26379     createMonthPicker : function(){
26380         if(!this.monthPicker.dom.firstChild){
26381             var buf = ['<table border="0" cellspacing="0">'];
26382             for(var i = 0; i < 6; i++){
26383                 buf.push(
26384                     '<tr><td class="x-date-mp-month"><a href="#">', Date.getShortMonthName(i), '</a></td>',
26385                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', Date.getShortMonthName(i + 6), '</a></td>',
26386                     i === 0 ?
26387                     '<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>' :
26388                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26389                 );
26390             }
26391             buf.push(
26392                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26393                     this.okText,
26394                     '</button><button type="button" class="x-date-mp-cancel">',
26395                     this.cancelText,
26396                     '</button></td></tr>',
26397                 '</table>'
26398             );
26399             this.monthPicker.update(buf.join(''));
26400
26401             this.mon(this.monthPicker, 'click', this.onMonthClick, this);
26402             this.mon(this.monthPicker, 'dblclick', this.onMonthDblClick, this);
26403
26404             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26405             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26406
26407             this.mpMonths.each(function(m, a, i){
26408                 i += 1;
26409                 if((i%2) === 0){
26410                     m.dom.xmonth = 5 + Math.round(i * 0.5);
26411                 }else{
26412                     m.dom.xmonth = Math.round((i-1) * 0.5);
26413                 }
26414             });
26415         }
26416     },
26417
26418     // private
26419     showMonthPicker : function(){
26420         if(!this.disabled){
26421             this.createMonthPicker();
26422             var size = this.el.getSize();
26423             this.monthPicker.setSize(size);
26424             this.monthPicker.child('table').setSize(size);
26425
26426             this.mpSelMonth = (this.activeDate || this.value).getMonth();
26427             this.updateMPMonth(this.mpSelMonth);
26428             this.mpSelYear = (this.activeDate || this.value).getFullYear();
26429             this.updateMPYear(this.mpSelYear);
26430
26431             this.monthPicker.slideIn('t', {duration:0.2});
26432         }
26433     },
26434
26435     // private
26436     updateMPYear : function(y){
26437         this.mpyear = y;
26438         var ys = this.mpYears.elements;
26439         for(var i = 1; i <= 10; i++){
26440             var td = ys[i-1], y2;
26441             if((i%2) === 0){
26442                 y2 = y + Math.round(i * 0.5);
26443                 td.firstChild.innerHTML = y2;
26444                 td.xyear = y2;
26445             }else{
26446                 y2 = y - (5-Math.round(i * 0.5));
26447                 td.firstChild.innerHTML = y2;
26448                 td.xyear = y2;
26449             }
26450             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26451         }
26452     },
26453
26454     // private
26455     updateMPMonth : function(sm){
26456         this.mpMonths.each(function(m, a, i){
26457             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26458         });
26459     },
26460
26461     // private
26462     selectMPMonth : function(m){
26463
26464     },
26465
26466     // private
26467     onMonthClick : function(e, t){
26468         e.stopEvent();
26469         var el = new Ext.Element(t), pn;
26470         if(el.is('button.x-date-mp-cancel')){
26471             this.hideMonthPicker();
26472         }
26473         else if(el.is('button.x-date-mp-ok')){
26474             var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
26475             if(d.getMonth() != this.mpSelMonth){
26476                 // 'fix' the JS rolling date conversion if needed
26477                 d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
26478             }
26479             this.update(d);
26480             this.hideMonthPicker();
26481         }
26482         else if((pn = el.up('td.x-date-mp-month', 2))){
26483             this.mpMonths.removeClass('x-date-mp-sel');
26484             pn.addClass('x-date-mp-sel');
26485             this.mpSelMonth = pn.dom.xmonth;
26486         }
26487         else if((pn = el.up('td.x-date-mp-year', 2))){
26488             this.mpYears.removeClass('x-date-mp-sel');
26489             pn.addClass('x-date-mp-sel');
26490             this.mpSelYear = pn.dom.xyear;
26491         }
26492         else if(el.is('a.x-date-mp-prev')){
26493             this.updateMPYear(this.mpyear-10);
26494         }
26495         else if(el.is('a.x-date-mp-next')){
26496             this.updateMPYear(this.mpyear+10);
26497         }
26498     },
26499
26500     // private
26501     onMonthDblClick : function(e, t){
26502         e.stopEvent();
26503         var el = new Ext.Element(t), pn;
26504         if((pn = el.up('td.x-date-mp-month', 2))){
26505             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26506             this.hideMonthPicker();
26507         }
26508         else if((pn = el.up('td.x-date-mp-year', 2))){
26509             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26510             this.hideMonthPicker();
26511         }
26512     },
26513
26514     // private
26515     hideMonthPicker : function(disableAnim){
26516         if(this.monthPicker){
26517             if(disableAnim === true){
26518                 this.monthPicker.hide();
26519             }else{
26520                 this.monthPicker.slideOut('t', {duration:0.2});
26521             }
26522         }
26523     },
26524
26525     // private
26526     showPrevMonth : function(e){
26527         this.update(this.activeDate.add('mo', -1));
26528     },
26529
26530     // private
26531     showNextMonth : function(e){
26532         this.update(this.activeDate.add('mo', 1));
26533     },
26534
26535     // private
26536     showPrevYear : function(){
26537         this.update(this.activeDate.add('y', -1));
26538     },
26539
26540     // private
26541     showNextYear : function(){
26542         this.update(this.activeDate.add('y', 1));
26543     },
26544
26545     // private
26546     handleMouseWheel : function(e){
26547         e.stopEvent();
26548         if(!this.disabled){
26549             var delta = e.getWheelDelta();
26550             if(delta > 0){
26551                 this.showPrevMonth();
26552             } else if(delta < 0){
26553                 this.showNextMonth();
26554             }
26555         }
26556     },
26557
26558     // private
26559     handleDateClick : function(e, t){
26560         e.stopEvent();
26561         if(!this.disabled && t.dateValue && !Ext.fly(t.parentNode).hasClass('x-date-disabled')){
26562             this.cancelFocus = this.focusOnSelect === false;
26563             this.setValue(new Date(t.dateValue));
26564             delete this.cancelFocus;
26565             this.fireEvent('select', this, this.value);
26566         }
26567     },
26568
26569     // private
26570     selectToday : function(){
26571         if(this.todayBtn && !this.todayBtn.disabled){
26572             this.setValue(new Date().clearTime());
26573             this.fireEvent('select', this, this.value);
26574         }
26575     },
26576
26577     // private
26578     update : function(date, forceRefresh){
26579         if(this.rendered){
26580             var vd = this.activeDate, vis = this.isVisible();
26581             this.activeDate = date;
26582             if(!forceRefresh && vd && this.el){
26583                 var t = date.getTime();
26584                 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26585                     this.cells.removeClass('x-date-selected');
26586                     this.cells.each(function(c){
26587                        if(c.dom.firstChild.dateValue == t){
26588                            c.addClass('x-date-selected');
26589                            if(vis && !this.cancelFocus){
26590                                Ext.fly(c.dom.firstChild).focus(50);
26591                            }
26592                            return false;
26593                        }
26594                     }, this);
26595                     return;
26596                 }
26597             }
26598             var days = date.getDaysInMonth(),
26599                 firstOfMonth = date.getFirstDateOfMonth(),
26600                 startingPos = firstOfMonth.getDay()-this.startDay;
26601
26602             if(startingPos < 0){
26603                 startingPos += 7;
26604             }
26605             days += startingPos;
26606
26607             var pm = date.add('mo', -1),
26608                 prevStart = pm.getDaysInMonth()-startingPos,
26609                 cells = this.cells.elements,
26610                 textEls = this.textNodes,
26611                 // convert everything to numbers so it's fast
26612                 d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart, this.initHour)),
26613                 today = new Date().clearTime().getTime(),
26614                 sel = date.clearTime(true).getTime(),
26615                 min = this.minDate ? this.minDate.clearTime(true) : Number.NEGATIVE_INFINITY,
26616                 max = this.maxDate ? this.maxDate.clearTime(true) : Number.POSITIVE_INFINITY,
26617                 ddMatch = this.disabledDatesRE,
26618                 ddText = this.disabledDatesText,
26619                 ddays = this.disabledDays ? this.disabledDays.join('') : false,
26620                 ddaysText = this.disabledDaysText,
26621                 format = this.format;
26622
26623             if(this.showToday){
26624                 var td = new Date().clearTime(),
26625                     disable = (td < min || td > max ||
26626                     (ddMatch && format && ddMatch.test(td.dateFormat(format))) ||
26627                     (ddays && ddays.indexOf(td.getDay()) != -1));
26628
26629                 if(!this.disabled){
26630                     this.todayBtn.setDisabled(disable);
26631                     this.todayKeyListener[disable ? 'disable' : 'enable']();
26632                 }
26633             }
26634
26635             var setCellClass = function(cal, cell){
26636                 cell.title = '';
26637                 var t = d.clearTime(true).getTime();
26638                 cell.firstChild.dateValue = t;
26639                 if(t == today){
26640                     cell.className += ' x-date-today';
26641                     cell.title = cal.todayText;
26642                 }
26643                 if(t == sel){
26644                     cell.className += ' x-date-selected';
26645                     if(vis){
26646                         Ext.fly(cell.firstChild).focus(50);
26647                     }
26648                 }
26649                 // disabling
26650                 if(t < min) {
26651                     cell.className = ' x-date-disabled';
26652                     cell.title = cal.minText;
26653                     return;
26654                 }
26655                 if(t > max) {
26656                     cell.className = ' x-date-disabled';
26657                     cell.title = cal.maxText;
26658                     return;
26659                 }
26660                 if(ddays){
26661                     if(ddays.indexOf(d.getDay()) != -1){
26662                         cell.title = ddaysText;
26663                         cell.className = ' x-date-disabled';
26664                     }
26665                 }
26666                 if(ddMatch && format){
26667                     var fvalue = d.dateFormat(format);
26668                     if(ddMatch.test(fvalue)){
26669                         cell.title = ddText.replace('%0', fvalue);
26670                         cell.className = ' x-date-disabled';
26671                     }
26672                 }
26673             };
26674
26675             var i = 0;
26676             for(; i < startingPos; i++) {
26677                 textEls[i].innerHTML = (++prevStart);
26678                 d.setDate(d.getDate()+1);
26679                 cells[i].className = 'x-date-prevday';
26680                 setCellClass(this, cells[i]);
26681             }
26682             for(; i < days; i++){
26683                 var intDay = i - startingPos + 1;
26684                 textEls[i].innerHTML = (intDay);
26685                 d.setDate(d.getDate()+1);
26686                 cells[i].className = 'x-date-active';
26687                 setCellClass(this, cells[i]);
26688             }
26689             var extraDays = 0;
26690             for(; i < 42; i++) {
26691                  textEls[i].innerHTML = (++extraDays);
26692                  d.setDate(d.getDate()+1);
26693                  cells[i].className = 'x-date-nextday';
26694                  setCellClass(this, cells[i]);
26695             }
26696
26697             this.mbtn.setText(this.monthNames[date.getMonth()] + ' ' + date.getFullYear());
26698
26699             if(!this.internalRender){
26700                 var main = this.el.dom.firstChild,
26701                     w = main.offsetWidth;
26702                 this.el.setWidth(w + this.el.getBorderWidth('lr'));
26703                 Ext.fly(main).setWidth(w);
26704                 this.internalRender = true;
26705                 // opera does not respect the auto grow header center column
26706                 // then, after it gets a width opera refuses to recalculate
26707                 // without a second pass
26708                 if(Ext.isOpera && !this.secondPass){
26709                     main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + 'px';
26710                     this.secondPass = true;
26711                     this.update.defer(10, this, [date]);
26712                 }
26713             }
26714         }
26715     },
26716
26717     // private
26718     beforeDestroy : function() {
26719         if(this.rendered){
26720             Ext.destroy(
26721                 this.keyNav,
26722                 this.monthPicker,
26723                 this.eventEl,
26724                 this.mbtn,
26725                 this.nextRepeater,
26726                 this.prevRepeater,
26727                 this.cells.el,
26728                 this.todayBtn
26729             );
26730             delete this.textNodes;
26731             delete this.cells.elements;
26732         }
26733     }
26734
26735     /**
26736      * @cfg {String} autoEl @hide
26737      */
26738 });
26739
26740 Ext.reg('datepicker', Ext.DatePicker);
26741 /**
26742  * @class Ext.LoadMask
26743  * A simple utility class for generically masking elements while loading data.  If the {@link #store}
26744  * config option is specified, the masking will be automatically synchronized with the store's loading
26745  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
26746  * element's Updater load indicator and will be destroyed after the initial load.
26747  * <p>Example usage:</p>
26748  *<pre><code>
26749 // Basic mask:
26750 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
26751 myMask.show();
26752 </code></pre>
26753  * @constructor
26754  * Create a new LoadMask
26755  * @param {Mixed} el The element or DOM node, or its id
26756  * @param {Object} config The config object
26757  */
26758 Ext.LoadMask = function(el, config){
26759     this.el = Ext.get(el);
26760     Ext.apply(this, config);
26761     if(this.store){
26762         this.store.on({
26763             scope: this,
26764             beforeload: this.onBeforeLoad,
26765             load: this.onLoad,
26766             exception: this.onLoad
26767         });
26768         this.removeMask = Ext.value(this.removeMask, false);
26769     }else{
26770         var um = this.el.getUpdater();
26771         um.showLoadIndicator = false; // disable the default indicator
26772         um.on({
26773             scope: this,
26774             beforeupdate: this.onBeforeLoad,
26775             update: this.onLoad,
26776             failure: this.onLoad
26777         });
26778         this.removeMask = Ext.value(this.removeMask, true);
26779     }
26780 };
26781
26782 Ext.LoadMask.prototype = {
26783     /**
26784      * @cfg {Ext.data.Store} store
26785      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
26786      * hidden on either load sucess, or load fail.
26787      */
26788     /**
26789      * @cfg {Boolean} removeMask
26790      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
26791      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
26792      */
26793     /**
26794      * @cfg {String} msg
26795      * The text to display in a centered loading message box (defaults to 'Loading...')
26796      */
26797     msg : 'Loading...',
26798     /**
26799      * @cfg {String} msgCls
26800      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
26801      */
26802     msgCls : 'x-mask-loading',
26803
26804     /**
26805      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
26806      * @type Boolean
26807      */
26808     disabled: false,
26809
26810     /**
26811      * Disables the mask to prevent it from being displayed
26812      */
26813     disable : function(){
26814        this.disabled = true;
26815     },
26816
26817     /**
26818      * Enables the mask so that it can be displayed
26819      */
26820     enable : function(){
26821         this.disabled = false;
26822     },
26823
26824     // private
26825     onLoad : function(){
26826         this.el.unmask(this.removeMask);
26827     },
26828
26829     // private
26830     onBeforeLoad : function(){
26831         if(!this.disabled){
26832             this.el.mask(this.msg, this.msgCls);
26833         }
26834     },
26835
26836     /**
26837      * Show this LoadMask over the configured Element.
26838      */
26839     show: function(){
26840         this.onBeforeLoad();
26841     },
26842
26843     /**
26844      * Hide this LoadMask.
26845      */
26846     hide: function(){
26847         this.onLoad();
26848     },
26849
26850     // private
26851     destroy : function(){
26852         if(this.store){
26853             this.store.un('beforeload', this.onBeforeLoad, this);
26854             this.store.un('load', this.onLoad, this);
26855             this.store.un('exception', this.onLoad, this);
26856         }else{
26857             var um = this.el.getUpdater();
26858             um.un('beforeupdate', this.onBeforeLoad, this);
26859             um.un('update', this.onLoad, this);
26860             um.un('failure', this.onLoad, this);
26861         }
26862     }
26863 };Ext.ns('Ext.slider');
26864
26865 /**
26866  * @class Ext.slider.Thumb
26867  * @extends Object
26868  * Represents a single thumb element on a Slider. This would not usually be created manually and would instead
26869  * be created internally by an {@link Ext.slider.MultiSlider Ext.Slider}.
26870  */
26871 Ext.slider.Thumb = Ext.extend(Object, {
26872
26873     /**
26874      * @constructor
26875      * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
26876      */
26877     constructor: function(config) {
26878         /**
26879          * @property slider
26880          * @type Ext.slider.MultiSlider
26881          * The slider this thumb is contained within
26882          */
26883         Ext.apply(this, config || {}, {
26884             cls: 'x-slider-thumb',
26885
26886             /**
26887              * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings
26888              */
26889             constrain: false
26890         });
26891
26892         Ext.slider.Thumb.superclass.constructor.call(this, config);
26893
26894         if (this.slider.vertical) {
26895             Ext.apply(this, Ext.slider.Thumb.Vertical);
26896         }
26897     },
26898
26899     /**
26900      * Renders the thumb into a slider
26901      */
26902     render: function() {
26903         this.el = this.slider.innerEl.insertFirst({cls: this.cls});
26904
26905         this.initEvents();
26906     },
26907
26908     /**
26909      * Enables the thumb if it is currently disabled
26910      */
26911     enable: function() {
26912         this.disabled = false;
26913         this.el.removeClass(this.slider.disabledClass);
26914     },
26915
26916     /**
26917      * Disables the thumb if it is currently enabled
26918      */
26919     disable: function() {
26920         this.disabled = true;
26921         this.el.addClass(this.slider.disabledClass);
26922     },
26923
26924     /**
26925      * Sets up an Ext.dd.DragTracker for this thumb
26926      */
26927     initEvents: function() {
26928         var el = this.el;
26929
26930         el.addClassOnOver('x-slider-thumb-over');
26931
26932         this.tracker = new Ext.dd.DragTracker({
26933             onBeforeStart: this.onBeforeDragStart.createDelegate(this),
26934             onStart      : this.onDragStart.createDelegate(this),
26935             onDrag       : this.onDrag.createDelegate(this),
26936             onEnd        : this.onDragEnd.createDelegate(this),
26937             tolerance    : 3,
26938             autoStart    : 300
26939         });
26940
26941         this.tracker.initEl(el);
26942     },
26943
26944     /**
26945      * @private
26946      * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled,
26947      * this returns false to disable the DragTracker too.
26948      * @return {Boolean} False if the slider is currently disabled
26949      */
26950     onBeforeDragStart : function(e) {
26951         if (this.disabled) {
26952             return false;
26953         } else {
26954             this.slider.promoteThumb(this);
26955             return true;
26956         }
26957     },
26958
26959     /**
26960      * @private
26961      * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class
26962      * to the thumb and fires the 'dragstart' event
26963      */
26964     onDragStart: function(e){
26965         this.el.addClass('x-slider-thumb-drag');
26966         this.dragging = true;
26967         this.dragStartValue = this.value;
26968
26969         this.slider.fireEvent('dragstart', this.slider, e, this);
26970     },
26971
26972     /**
26973      * @private
26974      * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time
26975      * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag
26976      */
26977     onDrag: function(e) {
26978         var slider   = this.slider,
26979             index    = this.index,
26980             newValue = this.getNewValue();
26981
26982         if (this.constrain) {
26983             var above = slider.thumbs[index + 1],
26984                 below = slider.thumbs[index - 1];
26985
26986             if (below != undefined && newValue <= below.value) newValue = below.value;
26987             if (above != undefined && newValue >= above.value) newValue = above.value;
26988         }
26989
26990         slider.setValue(index, newValue, false);
26991         slider.fireEvent('drag', slider, e, this);
26992     },
26993
26994     getNewValue: function() {
26995         var slider   = this.slider,
26996             pos      = slider.innerEl.translatePoints(this.tracker.getXY());
26997
26998         return Ext.util.Format.round(slider.reverseValue(pos.left), slider.decimalPrecision);
26999     },
27000
27001     /**
27002      * @private
27003      * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and
27004      * fires the 'changecomplete' event with the new value
27005      */
27006     onDragEnd: function(e) {
27007         var slider = this.slider,
27008             value  = this.value;
27009
27010         this.el.removeClass('x-slider-thumb-drag');
27011
27012         this.dragging = false;
27013         slider.fireEvent('dragend', slider, e);
27014
27015         if (this.dragStartValue != value) {
27016             slider.fireEvent('changecomplete', slider, value, this);
27017         }
27018     }
27019 });
27020
27021 /**
27022  * @class Ext.slider.MultiSlider
27023  * @extends Ext.BoxComponent
27024  * 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:
27025 <pre>
27026 new Ext.Slider({
27027     renderTo: Ext.getBody(),
27028     width: 200,
27029     value: 50,
27030     increment: 10,
27031     minValue: 0,
27032     maxValue: 100
27033 });
27034 </pre>
27035  * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
27036 <pre>
27037 new Ext.Slider({
27038     renderTo: Ext.getBody(),
27039     width: 200,
27040     values: [25, 50, 75],
27041     minValue: 0,
27042     maxValue: 100,
27043
27044     //this defaults to true, setting to false allows the thumbs to pass each other
27045     {@link #constrainThumbs}: false
27046 });
27047 </pre>
27048  */
27049 Ext.slider.MultiSlider = Ext.extend(Ext.BoxComponent, {
27050     /**
27051      * @cfg {Number} value The value to initialize the slider with. Defaults to minValue.
27052      */
27053     /**
27054      * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
27055      */
27056     vertical: false,
27057     /**
27058      * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
27059      */
27060     minValue: 0,
27061     /**
27062      * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
27063      */
27064     maxValue: 100,
27065     /**
27066      * @cfg {Number/Boolean} decimalPrecision.
27067      * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>
27068      * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>
27069      */
27070     decimalPrecision: 0,
27071     /**
27072      * @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.
27073      */
27074     keyIncrement: 1,
27075     /**
27076      * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
27077      */
27078     increment: 0,
27079
27080     /**
27081      * @private
27082      * @property clickRange
27083      * @type Array
27084      * 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],
27085      * 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'
27086      * 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
27087      */
27088     clickRange: [5,15],
27089
27090     /**
27091      * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
27092      */
27093     clickToChange : true,
27094     /**
27095      * @cfg {Boolean} animate Turn on or off animation. Defaults to true
27096      */
27097     animate: true,
27098
27099     /**
27100      * True while the thumb is in a drag operation
27101      * @type Boolean
27102      */
27103     dragging: false,
27104
27105     /**
27106      * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
27107      */
27108     constrainThumbs: true,
27109
27110     /**
27111      * @private
27112      * @property topThumbZIndex
27113      * @type Number
27114      * The number used internally to set the z index of the top thumb (see promoteThumb for details)
27115      */
27116     topThumbZIndex: 10000,
27117
27118     // private override
27119     initComponent : function(){
27120         if(!Ext.isDefined(this.value)){
27121             this.value = this.minValue;
27122         }
27123
27124         /**
27125          * @property thumbs
27126          * @type Array
27127          * Array containing references to each thumb
27128          */
27129         this.thumbs = [];
27130
27131         Ext.slider.MultiSlider.superclass.initComponent.call(this);
27132
27133         this.keyIncrement = Math.max(this.increment, this.keyIncrement);
27134         this.addEvents(
27135             /**
27136              * @event beforechange
27137              * Fires before the slider value is changed. By returning false from an event handler,
27138              * you can cancel the event and prevent the slider from changing.
27139              * @param {Ext.Slider} slider The slider
27140              * @param {Number} newValue The new value which the slider is being changed to.
27141              * @param {Number} oldValue The old value which the slider was previously.
27142              */
27143             'beforechange',
27144
27145             /**
27146              * @event change
27147              * Fires when the slider value is changed.
27148              * @param {Ext.Slider} slider The slider
27149              * @param {Number} newValue The new value which the slider has been changed to.
27150              * @param {Ext.slider.Thumb} thumb The thumb that was changed
27151              */
27152             'change',
27153
27154             /**
27155              * @event changecomplete
27156              * Fires when the slider value is changed by the user and any drag operations have completed.
27157              * @param {Ext.Slider} slider The slider
27158              * @param {Number} newValue The new value which the slider has been changed to.
27159              * @param {Ext.slider.Thumb} thumb The thumb that was changed
27160              */
27161             'changecomplete',
27162
27163             /**
27164              * @event dragstart
27165              * Fires after a drag operation has started.
27166              * @param {Ext.Slider} slider The slider
27167              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
27168              */
27169             'dragstart',
27170
27171             /**
27172              * @event drag
27173              * Fires continuously during the drag operation while the mouse is moving.
27174              * @param {Ext.Slider} slider The slider
27175              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
27176              */
27177             'drag',
27178
27179             /**
27180              * @event dragend
27181              * Fires after the drag operation has completed.
27182              * @param {Ext.Slider} slider The slider
27183              * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
27184              */
27185             'dragend'
27186         );
27187
27188         /**
27189          * @property values
27190          * @type Array
27191          * Array of values to initalize the thumbs with
27192          */
27193         if (this.values == undefined || Ext.isEmpty(this.values)) this.values = [0];
27194
27195         var values = this.values;
27196
27197         for (var i=0; i < values.length; i++) {
27198             this.addThumb(values[i]);
27199         }
27200
27201         if(this.vertical){
27202             Ext.apply(this, Ext.slider.Vertical);
27203         }
27204     },
27205
27206     /**
27207      * Creates a new thumb and adds it to the slider
27208      * @param {Number} value The initial value to set on the thumb. Defaults to 0
27209      */
27210     addThumb: function(value) {
27211         var thumb = new Ext.slider.Thumb({
27212             value    : value,
27213             slider   : this,
27214             index    : this.thumbs.length,
27215             constrain: this.constrainThumbs
27216         });
27217         this.thumbs.push(thumb);
27218
27219         //render the thumb now if needed
27220         if (this.rendered) thumb.render();
27221     },
27222
27223     /**
27224      * @private
27225      * Moves the given thumb above all other by increasing its z-index. This is called when as drag
27226      * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
27227      * required when the thumbs are stacked on top of each other at one of the ends of the slider's
27228      * range, which can result in the user not being able to move any of them.
27229      * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
27230      */
27231     promoteThumb: function(topThumb) {
27232         var thumbs = this.thumbs,
27233             zIndex, thumb;
27234
27235         for (var i = 0, j = thumbs.length; i < j; i++) {
27236             thumb = thumbs[i];
27237
27238             if (thumb == topThumb) {
27239                 zIndex = this.topThumbZIndex;
27240             } else {
27241                 zIndex = '';
27242             }
27243
27244             thumb.el.setStyle('zIndex', zIndex);
27245         }
27246     },
27247
27248     // private override
27249     onRender : function() {
27250         this.autoEl = {
27251             cls: 'x-slider ' + (this.vertical ? 'x-slider-vert' : 'x-slider-horz'),
27252             cn : {
27253                 cls: 'x-slider-end',
27254                 cn : {
27255                     cls:'x-slider-inner',
27256                     cn : [{tag:'a', cls:'x-slider-focus', href:"#", tabIndex: '-1', hidefocus:'on'}]
27257                 }
27258             }
27259         };
27260
27261         Ext.slider.MultiSlider.superclass.onRender.apply(this, arguments);
27262
27263         this.endEl   = this.el.first();
27264         this.innerEl = this.endEl.first();
27265         this.focusEl = this.innerEl.child('.x-slider-focus');
27266
27267         //render each thumb
27268         for (var i=0; i < this.thumbs.length; i++) {
27269             this.thumbs[i].render();
27270         }
27271
27272         //calculate the size of half a thumb
27273         var thumb      = this.innerEl.child('.x-slider-thumb');
27274         this.halfThumb = (this.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
27275
27276         this.initEvents();
27277     },
27278
27279     /**
27280      * @private
27281      * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
27282      * Creates a new DragTracker which is used to control what happens when the user drags the thumb around.
27283      */
27284     initEvents : function(){
27285         this.mon(this.el, {
27286             scope    : this,
27287             mousedown: this.onMouseDown,
27288             keydown  : this.onKeyDown
27289         });
27290
27291         this.focusEl.swallowEvent("click", true);
27292     },
27293
27294     /**
27295      * @private
27296      * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
27297      * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
27298      * @param {Ext.EventObject} e The click event
27299      */
27300     onMouseDown : function(e){
27301         if(this.disabled){
27302             return;
27303         }
27304
27305         //see if the click was on any of the thumbs
27306         var thumbClicked = false;
27307         for (var i=0; i < this.thumbs.length; i++) {
27308             thumbClicked = thumbClicked || e.target == this.thumbs[i].el.dom;
27309         }
27310
27311         if (this.clickToChange && !thumbClicked) {
27312             var local = this.innerEl.translatePoints(e.getXY());
27313             this.onClickChange(local);
27314         }
27315         this.focus();
27316     },
27317
27318     /**
27319      * @private
27320      * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Vertical.
27321      * Only changes the value if the click was within this.clickRange.
27322      * @param {Object} local Object containing top and left values for the click event.
27323      */
27324     onClickChange : function(local) {
27325         if (local.top > this.clickRange[0] && local.top < this.clickRange[1]) {
27326             //find the nearest thumb to the click event
27327             var thumb = this.getNearest(local, 'left'),
27328                 index = thumb.index;
27329
27330             this.setValue(index, Ext.util.Format.round(this.reverseValue(local.left), this.decimalPrecision), undefined, true);
27331         }
27332     },
27333
27334     /**
27335      * @private
27336      * Returns the nearest thumb to a click event, along with its distance
27337      * @param {Object} local Object containing top and left values from a click event
27338      * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
27339      * @return {Object} The closest thumb object and its distance from the click event
27340      */
27341     getNearest: function(local, prop) {
27342         var localValue = prop == 'top' ? this.innerEl.getHeight() - local[prop] : local[prop],
27343             clickValue = this.reverseValue(localValue),
27344             nearestDistance = (this.maxValue - this.minValue) + 5, //add a small fudge for the end of the slider 
27345             index = 0,
27346             nearest = null;
27347
27348         for (var i=0; i < this.thumbs.length; i++) {
27349             var thumb = this.thumbs[i],
27350                 value = thumb.value,
27351                 dist  = Math.abs(value - clickValue);
27352
27353             if (Math.abs(dist <= nearestDistance)) {
27354                 nearest = thumb;
27355                 index = i;
27356                 nearestDistance = dist;
27357             }
27358         }
27359         return nearest;
27360     },
27361
27362     /**
27363      * @private
27364      * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
27365      * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
27366      * @param {Ext.EventObject} e The Event object
27367      */
27368     onKeyDown : function(e){
27369         /*
27370          * The behaviour for keyboard handling with multiple thumbs is currently undefined.
27371          * There's no real sane default for it, so leave it like this until we come up
27372          * with a better way of doing it.
27373          */
27374         if(this.disabled || this.thumbs.length !== 1){
27375             e.preventDefault();
27376             return;
27377         }
27378         var k = e.getKey(),
27379             val;
27380         switch(k){
27381             case e.UP:
27382             case e.RIGHT:
27383                 e.stopEvent();
27384                 val = e.ctrlKey ? this.maxValue : this.getValue(0) + this.keyIncrement;
27385                 this.setValue(0, val, undefined, true);
27386             break;
27387             case e.DOWN:
27388             case e.LEFT:
27389                 e.stopEvent();
27390                 val = e.ctrlKey ? this.minValue : this.getValue(0) - this.keyIncrement;
27391                 this.setValue(0, val, undefined, true);
27392             break;
27393             default:
27394                 e.preventDefault();
27395         }
27396     },
27397
27398     /**
27399      * @private
27400      * If using snapping, this takes a desired new value and returns the closest snapped
27401      * value to it
27402      * @param {Number} value The unsnapped value
27403      * @return {Number} The value of the nearest snap target
27404      */
27405     doSnap : function(value){
27406         if (!(this.increment && value)) {
27407             return value;
27408         }
27409         var newValue = value,
27410             inc = this.increment,
27411             m = value % inc;
27412         if (m != 0) {
27413             newValue -= m;
27414             if (m * 2 >= inc) {
27415                 newValue += inc;
27416             } else if (m * 2 < -inc) {
27417                 newValue -= inc;
27418             }
27419         }
27420         return newValue.constrain(this.minValue,  this.maxValue);
27421     },
27422
27423     // private
27424     afterRender : function(){
27425         Ext.slider.MultiSlider.superclass.afterRender.apply(this, arguments);
27426
27427         for (var i=0; i < this.thumbs.length; i++) {
27428             var thumb = this.thumbs[i];
27429
27430             if (thumb.value !== undefined) {
27431                 var v = this.normalizeValue(thumb.value);
27432
27433                 if (v !== thumb.value) {
27434                     // delete this.value;
27435                     this.setValue(i, v, false);
27436                 } else {
27437                     this.moveThumb(i, this.translateValue(v), false);
27438                 }
27439             }
27440         };
27441     },
27442
27443     /**
27444      * @private
27445      * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
27446      * the ratio is 2
27447      * @return {Number} The ratio of pixels to mapped values
27448      */
27449     getRatio : function(){
27450         var w = this.innerEl.getWidth(),
27451             v = this.maxValue - this.minValue;
27452         return v == 0 ? w : (w/v);
27453     },
27454
27455     /**
27456      * @private
27457      * Returns a snapped, constrained value when given a desired value
27458      * @param {Number} value Raw number value
27459      * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
27460      */
27461     normalizeValue : function(v){
27462         v = this.doSnap(v);
27463         v = Ext.util.Format.round(v, this.decimalPrecision);
27464         v = v.constrain(this.minValue, this.maxValue);
27465         return v;
27466     },
27467
27468     /**
27469      * Sets the minimum value for the slider instance. If the current value is less than the
27470      * minimum value, the current value will be changed.
27471      * @param {Number} val The new minimum value
27472      */
27473     setMinValue : function(val){
27474         this.minValue = val;
27475         var i = 0,
27476             thumbs = this.thumbs,
27477             len = thumbs.length,
27478             t;
27479             
27480         for(; i < len; ++i){
27481             t = thumbs[i];
27482             t.value = t.value < val ? val : t.value;
27483         }
27484         this.syncThumb();
27485     },
27486
27487     /**
27488      * Sets the maximum value for the slider instance. If the current value is more than the
27489      * maximum value, the current value will be changed.
27490      * @param {Number} val The new maximum value
27491      */
27492     setMaxValue : function(val){
27493         this.maxValue = val;
27494         var i = 0,
27495             thumbs = this.thumbs,
27496             len = thumbs.length,
27497             t;
27498             
27499         for(; i < len; ++i){
27500             t = thumbs[i];
27501             t.value = t.value > val ? val : t.value;
27502         }
27503         this.syncThumb();
27504     },
27505
27506     /**
27507      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
27508      * the minValue and maxValue.
27509      * @param {Number} index Index of the thumb to move
27510      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
27511      * @param {Boolean} animate Turn on or off animation, defaults to true
27512      */
27513     setValue : function(index, v, animate, changeComplete) {
27514         var thumb = this.thumbs[index],
27515             el    = thumb.el;
27516
27517         v = this.normalizeValue(v);
27518
27519         if (v !== thumb.value && this.fireEvent('beforechange', this, v, thumb.value, thumb) !== false) {
27520             thumb.value = v;
27521             if(this.rendered){
27522                 this.moveThumb(index, this.translateValue(v), animate !== false);
27523                 this.fireEvent('change', this, v, thumb);
27524                 if(changeComplete){
27525                     this.fireEvent('changecomplete', this, v, thumb);
27526                 }
27527             }
27528         }
27529     },
27530
27531     /**
27532      * @private
27533      */
27534     translateValue : function(v) {
27535         var ratio = this.getRatio();
27536         return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
27537     },
27538
27539     /**
27540      * @private
27541      * Given a pixel location along the slider, returns the mapped slider value for that pixel.
27542      * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
27543      * returns 200
27544      * @param {Number} pos The position along the slider to return a mapped value for
27545      * @return {Number} The mapped value for the given position
27546      */
27547     reverseValue : function(pos){
27548         var ratio = this.getRatio();
27549         return (pos + (this.minValue * ratio)) / ratio;
27550     },
27551
27552     /**
27553      * @private
27554      * @param {Number} index Index of the thumb to move
27555      */
27556     moveThumb: function(index, v, animate){
27557         var thumb = this.thumbs[index].el;
27558
27559         if(!animate || this.animate === false){
27560             thumb.setLeft(v);
27561         }else{
27562             thumb.shift({left: v, stopFx: true, duration:.35});
27563         }
27564     },
27565
27566     // private
27567     focus : function(){
27568         this.focusEl.focus(10);
27569     },
27570
27571     // private
27572     onResize : function(w, h){
27573         var thumbs = this.thumbs,
27574             len = thumbs.length,
27575             i = 0;
27576             
27577         /*
27578          * If we happen to be animating during a resize, the position of the thumb will likely be off
27579          * when the animation stops. As such, just stop any animations before syncing the thumbs.
27580          */
27581         for(; i < len; ++i){
27582             thumbs[i].el.stopFx();    
27583         }
27584         this.innerEl.setWidth(w - (this.el.getPadding('l') + this.endEl.getPadding('r')));
27585         this.syncThumb();
27586         Ext.slider.MultiSlider.superclass.onResize.apply(this, arguments);
27587     },
27588
27589     //private
27590     onDisable: function(){
27591         Ext.slider.MultiSlider.superclass.onDisable.call(this);
27592
27593         for (var i=0; i < this.thumbs.length; i++) {
27594             var thumb = this.thumbs[i],
27595                 el    = thumb.el;
27596
27597             thumb.disable();
27598
27599             if(Ext.isIE){
27600                 //IE breaks when using overflow visible and opacity other than 1.
27601                 //Create a place holder for the thumb and display it.
27602                 var xy = el.getXY();
27603                 el.hide();
27604
27605                 this.innerEl.addClass(this.disabledClass).dom.disabled = true;
27606
27607                 if (!this.thumbHolder) {
27608                     this.thumbHolder = this.endEl.createChild({cls: 'x-slider-thumb ' + this.disabledClass});
27609                 }
27610
27611                 this.thumbHolder.show().setXY(xy);
27612             }
27613         }
27614     },
27615
27616     //private
27617     onEnable: function(){
27618         Ext.slider.MultiSlider.superclass.onEnable.call(this);
27619
27620         for (var i=0; i < this.thumbs.length; i++) {
27621             var thumb = this.thumbs[i],
27622                 el    = thumb.el;
27623
27624             thumb.enable();
27625
27626             if (Ext.isIE) {
27627                 this.innerEl.removeClass(this.disabledClass).dom.disabled = false;
27628
27629                 if (this.thumbHolder) this.thumbHolder.hide();
27630
27631                 el.show();
27632                 this.syncThumb();
27633             }
27634         }
27635     },
27636
27637     /**
27638      * Synchronizes the thumb position to the proper proportion of the total component width based
27639      * on the current slider {@link #value}.  This will be called automatically when the Slider
27640      * is resized by a layout, but if it is rendered auto width, this method can be called from
27641      * another resize handler to sync the Slider if necessary.
27642      */
27643     syncThumb : function() {
27644         if (this.rendered) {
27645             for (var i=0; i < this.thumbs.length; i++) {
27646                 this.moveThumb(i, this.translateValue(this.thumbs[i].value));
27647             }
27648         }
27649     },
27650
27651     /**
27652      * Returns the current value of the slider
27653      * @param {Number} index The index of the thumb to return a value for
27654      * @return {Number} The current value of the slider
27655      */
27656     getValue : function(index) {
27657         return this.thumbs[index].value;
27658     },
27659
27660     /**
27661      * Returns an array of values - one for the location of each thumb
27662      * @return {Array} The set of thumb values
27663      */
27664     getValues: function() {
27665         var values = [];
27666
27667         for (var i=0; i < this.thumbs.length; i++) {
27668             values.push(this.thumbs[i].value);
27669         }
27670
27671         return values;
27672     },
27673
27674     // private
27675     beforeDestroy : function(){
27676         Ext.destroyMembers(this, 'endEl', 'innerEl', 'thumb', 'halfThumb', 'focusEl', 'tracker', 'thumbHolder');
27677         Ext.slider.MultiSlider.superclass.beforeDestroy.call(this);
27678     }
27679 });
27680
27681 Ext.reg('multislider', Ext.slider.MultiSlider);
27682
27683 /**
27684  * @class Ext.slider.SingleSlider
27685  * @extends Ext.slider.MultiSlider
27686  * Slider which supports vertical or horizontal orientation, keyboard adjustments,
27687  * configurable snapping, axis clicking and animation. Can be added as an item to
27688  * any container. Example usage:
27689 <pre><code>
27690 new Ext.slider.SingleSlider({
27691     renderTo: Ext.getBody(),
27692     width: 200,
27693     value: 50,
27694     increment: 10,
27695     minValue: 0,
27696     maxValue: 100
27697 });
27698 </code></pre>
27699  * The class Ext.slider.SingleSlider is aliased to Ext.Slider for backwards compatibility.
27700  */
27701 Ext.slider.SingleSlider = Ext.extend(Ext.slider.MultiSlider, {
27702     constructor: function(config) {
27703       config = config || {};
27704
27705       Ext.applyIf(config, {
27706           values: [config.value || 0]
27707       });
27708
27709       Ext.slider.SingleSlider.superclass.constructor.call(this, config);
27710     },
27711
27712     /**
27713      * Returns the current value of the slider
27714      * @return {Number} The current value of the slider
27715      */
27716     getValue: function() {
27717         //just returns the value of the first thumb, which should be the only one in a single slider
27718         return Ext.slider.SingleSlider.superclass.getValue.call(this, 0);
27719     },
27720
27721     /**
27722      * Programmatically sets the value of the Slider. Ensures that the value is constrained within
27723      * the minValue and maxValue.
27724      * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
27725      * @param {Boolean} animate Turn on or off animation, defaults to true
27726      */
27727     setValue: function(value, animate) {
27728         var args = Ext.toArray(arguments),
27729             len  = args.length;
27730
27731         //this is to maintain backwards compatiblity for sliders with only one thunb. Usually you must pass the thumb
27732         //index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider
27733         //signature without the required index. The index will always be 0 for a single slider
27734         if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
27735             args.unshift(0);
27736         }
27737
27738         return Ext.slider.SingleSlider.superclass.setValue.apply(this, args);
27739     },
27740
27741     /**
27742      * Synchronizes the thumb position to the proper proportion of the total component width based
27743      * on the current slider {@link #value}.  This will be called automatically when the Slider
27744      * is resized by a layout, but if it is rendered auto width, this method can be called from
27745      * another resize handler to sync the Slider if necessary.
27746      */
27747     syncThumb : function() {
27748         return Ext.slider.SingleSlider.superclass.syncThumb.apply(this, [0].concat(arguments));
27749     },
27750     
27751     // private
27752     getNearest : function(){
27753         // Since there's only 1 thumb, it's always the nearest
27754         return this.thumbs[0];    
27755     }
27756 });
27757
27758 //backwards compatibility
27759 Ext.Slider = Ext.slider.SingleSlider;
27760
27761 Ext.reg('slider', Ext.slider.SingleSlider);
27762
27763 // private class to support vertical sliders
27764 Ext.slider.Vertical = {
27765     onResize : function(w, h){
27766         this.innerEl.setHeight(h - (this.el.getPadding('t') + this.endEl.getPadding('b')));
27767         this.syncThumb();
27768     },
27769
27770     getRatio : function(){
27771         var h = this.innerEl.getHeight(),
27772             v = this.maxValue - this.minValue;
27773         return h/v;
27774     },
27775
27776     moveThumb: function(index, v, animate) {
27777         var thumb = this.thumbs[index],
27778             el    = thumb.el;
27779
27780         if (!animate || this.animate === false) {
27781             el.setBottom(v);
27782         } else {
27783             el.shift({bottom: v, stopFx: true, duration:.35});
27784         }
27785     },
27786
27787     onClickChange : function(local) {
27788         if (local.left > this.clickRange[0] && local.left < this.clickRange[1]) {
27789             var thumb = this.getNearest(local, 'top'),
27790                 index = thumb.index,
27791                 value = this.minValue + this.reverseValue(this.innerEl.getHeight() - local.top);
27792
27793             this.setValue(index, Ext.util.Format.round(value, this.decimalPrecision), undefined, true);
27794         }
27795     }
27796 };
27797
27798 //private class to support vertical dragging of thumbs within a slider
27799 Ext.slider.Thumb.Vertical = {
27800     getNewValue: function() {
27801         var slider   = this.slider,
27802             innerEl  = slider.innerEl,
27803             pos      = innerEl.translatePoints(this.tracker.getXY()),
27804             bottom   = innerEl.getHeight() - pos.top;
27805
27806         return slider.minValue + Ext.util.Format.round(bottom / slider.getRatio(), slider.decimalPrecision);
27807     }
27808 };
27809 /**
27810  * @class Ext.ProgressBar
27811  * @extends Ext.BoxComponent
27812  * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>
27813  * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the
27814  * progress bar as needed from your own code.  This method is most appropriate when you want to show progress
27815  * throughout an operation that has predictable points of interest at which you can update the control.</p>
27816  * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it
27817  * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time
27818  * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in
27819  * which you have no need for indicating intermediate progress.</p>
27820  * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)
27821  * @cfg {String} text The progress bar text (defaults to '')
27822  * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress
27823  * bar's internal text element)
27824  * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)
27825  * @xtype progress
27826  */
27827 Ext.ProgressBar = Ext.extend(Ext.BoxComponent, {
27828    /**
27829     * @cfg {String} baseCls
27830     * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')
27831     */
27832     baseCls : 'x-progress',
27833     
27834     /**
27835     * @cfg {Boolean} animate
27836     * True to animate the progress bar during transitions (defaults to false)
27837     */
27838     animate : false,
27839
27840     // private
27841     waitTimer : null,
27842
27843     // private
27844     initComponent : function(){
27845         Ext.ProgressBar.superclass.initComponent.call(this);
27846         this.addEvents(
27847             /**
27848              * @event update
27849              * Fires after each update interval
27850              * @param {Ext.ProgressBar} this
27851              * @param {Number} The current progress value
27852              * @param {String} The current progress text
27853              */
27854             "update"
27855         );
27856     },
27857
27858     // private
27859     onRender : function(ct, position){
27860         var tpl = new Ext.Template(
27861             '<div class="{cls}-wrap">',
27862                 '<div class="{cls}-inner">',
27863                     '<div class="{cls}-bar">',
27864                         '<div class="{cls}-text">',
27865                             '<div>&#160;</div>',
27866                         '</div>',
27867                     '</div>',
27868                     '<div class="{cls}-text {cls}-text-back">',
27869                         '<div>&#160;</div>',
27870                     '</div>',
27871                 '</div>',
27872             '</div>'
27873         );
27874
27875         this.el = position ? tpl.insertBefore(position, {cls: this.baseCls}, true)
27876             : tpl.append(ct, {cls: this.baseCls}, true);
27877                 
27878         if(this.id){
27879             this.el.dom.id = this.id;
27880         }
27881         var inner = this.el.dom.firstChild;
27882         this.progressBar = Ext.get(inner.firstChild);
27883
27884         if(this.textEl){
27885             //use an external text el
27886             this.textEl = Ext.get(this.textEl);
27887             delete this.textTopEl;
27888         }else{
27889             //setup our internal layered text els
27890             this.textTopEl = Ext.get(this.progressBar.dom.firstChild);
27891             var textBackEl = Ext.get(inner.childNodes[1]);
27892             this.textTopEl.setStyle("z-index", 99).addClass('x-hidden');
27893             this.textEl = new Ext.CompositeElement([this.textTopEl.dom.firstChild, textBackEl.dom.firstChild]);
27894             this.textEl.setWidth(inner.offsetWidth);
27895         }
27896         this.progressBar.setHeight(inner.offsetHeight);
27897     },
27898     
27899     // private
27900     afterRender : function(){
27901         Ext.ProgressBar.superclass.afterRender.call(this);
27902         if(this.value){
27903             this.updateProgress(this.value, this.text);
27904         }else{
27905             this.updateText(this.text);
27906         }
27907     },
27908
27909     /**
27910      * Updates the progress bar value, and optionally its text.  If the text argument is not specified,
27911      * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even
27912      * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
27913      * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
27914      * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
27915      * @param {String} text (optional) The string to display in the progress text element (defaults to '')
27916      * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
27917      * not specified, the default for the class is used (default to false)
27918      * @return {Ext.ProgressBar} this
27919      */
27920     updateProgress : function(value, text, animate){
27921         this.value = value || 0;
27922         if(text){
27923             this.updateText(text);
27924         }
27925         if(this.rendered && !this.isDestroyed){
27926             var w = Math.floor(value*this.el.dom.firstChild.offsetWidth);
27927             this.progressBar.setWidth(w, animate === true || (animate !== false && this.animate));
27928             if(this.textTopEl){
27929                 //textTopEl should be the same width as the bar so overflow will clip as the bar moves
27930                 this.textTopEl.removeClass('x-hidden').setWidth(w);
27931             }
27932         }
27933         this.fireEvent('update', this, value, text);
27934         return this;
27935     },
27936
27937     /**
27938      * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress
27939      * bar will automatically reset after a fixed amount of time and optionally call a callback function
27940      * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must
27941      * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with
27942      * the following properties:
27943      * <pre>
27944 Property   Type          Description
27945 ---------- ------------  ----------------------------------------------------------------------
27946 duration   Number        The length of time in milliseconds that the progress bar should
27947                          run before resetting itself (defaults to undefined, in which case it
27948                          will run indefinitely until reset is called)
27949 interval   Number        The length of time in milliseconds between each progress update
27950                          (defaults to 1000 ms)
27951 animate    Boolean       Whether to animate the transition of the progress bar. If this value is
27952                          not specified, the default for the class is used.                                                   
27953 increment  Number        The number of progress update segments to display within the progress
27954                          bar (defaults to 10).  If the bar reaches the end and is still
27955                          updating, it will automatically wrap back to the beginning.
27956 text       String        Optional text to display in the progress bar element (defaults to '').
27957 fn         Function      A callback function to execute after the progress bar finishes auto-
27958                          updating.  The function will be called with no arguments.  This function
27959                          will be ignored if duration is not specified since in that case the
27960                          progress bar can only be stopped programmatically, so any required function
27961                          should be called by the same code after it resets the progress bar.
27962 scope      Object        The scope that is passed to the callback function (only applies when
27963                          duration and fn are both passed).
27964 </pre>
27965          *
27966          * Example usage:
27967          * <pre><code>
27968 var p = new Ext.ProgressBar({
27969    renderTo: 'my-el'
27970 });
27971
27972 //Wait for 5 seconds, then update the status el (progress bar will auto-reset)
27973 p.wait({
27974    interval: 100, //bar will move fast!
27975    duration: 5000,
27976    increment: 15,
27977    text: 'Updating...',
27978    scope: this,
27979    fn: function(){
27980       Ext.fly('status').update('Done!');
27981    }
27982 });
27983
27984 //Or update indefinitely until some async action completes, then reset manually
27985 p.wait();
27986 myAction.on('complete', function(){
27987     p.reset();
27988     Ext.fly('status').update('Done!');
27989 });
27990 </code></pre>
27991      * @param {Object} config (optional) Configuration options
27992      * @return {Ext.ProgressBar} this
27993      */
27994     wait : function(o){
27995         if(!this.waitTimer){
27996             var scope = this;
27997             o = o || {};
27998             this.updateText(o.text);
27999             this.waitTimer = Ext.TaskMgr.start({
28000                 run: function(i){
28001                     var inc = o.increment || 10;
28002                     i -= 1;
28003                     this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
28004                 },
28005                 interval: o.interval || 1000,
28006                 duration: o.duration,
28007                 onStop: function(){
28008                     if(o.fn){
28009                         o.fn.apply(o.scope || this);
28010                     }
28011                     this.reset();
28012                 },
28013                 scope: scope
28014             });
28015         }
28016         return this;
28017     },
28018
28019     /**
28020      * Returns true if the progress bar is currently in a {@link #wait} operation
28021      * @return {Boolean} True if waiting, else false
28022      */
28023     isWaiting : function(){
28024         return this.waitTimer !== null;
28025     },
28026
28027     /**
28028      * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress
28029      * bar itself will display the updated text.
28030      * @param {String} text (optional) The string to display in the progress text element (defaults to '')
28031      * @return {Ext.ProgressBar} this
28032      */
28033     updateText : function(text){
28034         this.text = text || '&#160;';
28035         if(this.rendered){
28036             this.textEl.update(this.text);
28037         }
28038         return this;
28039     },
28040     
28041     /**
28042      * Synchronizes the inner bar width to the proper proportion of the total componet width based
28043      * on the current progress {@link #value}.  This will be called automatically when the ProgressBar
28044      * is resized by a layout, but if it is rendered auto width, this method can be called from
28045      * another resize handler to sync the ProgressBar if necessary.
28046      */
28047     syncProgressBar : function(){
28048         if(this.value){
28049             this.updateProgress(this.value, this.text);
28050         }
28051         return this;
28052     },
28053
28054     /**
28055      * Sets the size of the progress bar.
28056      * @param {Number} width The new width in pixels
28057      * @param {Number} height The new height in pixels
28058      * @return {Ext.ProgressBar} this
28059      */
28060     setSize : function(w, h){
28061         Ext.ProgressBar.superclass.setSize.call(this, w, h);
28062         if(this.textTopEl){
28063             var inner = this.el.dom.firstChild;
28064             this.textEl.setSize(inner.offsetWidth, inner.offsetHeight);
28065         }
28066         this.syncProgressBar();
28067         return this;
28068     },
28069
28070     /**
28071      * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress
28072      * bar will also be hidden (using the {@link #hideMode} property internally).
28073      * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)
28074      * @return {Ext.ProgressBar} this
28075      */
28076     reset : function(hide){
28077         this.updateProgress(0);
28078         if(this.textTopEl){
28079             this.textTopEl.addClass('x-hidden');
28080         }
28081         this.clearTimer();
28082         if(hide === true){
28083             this.hide();
28084         }
28085         return this;
28086     },
28087     
28088     // private
28089     clearTimer : function(){
28090         if(this.waitTimer){
28091             this.waitTimer.onStop = null; //prevent recursion
28092             Ext.TaskMgr.stop(this.waitTimer);
28093             this.waitTimer = null;
28094         }
28095     },
28096     
28097     onDestroy: function(){
28098         this.clearTimer();
28099         if(this.rendered){
28100             if(this.textEl.isComposite){
28101                 this.textEl.clear();
28102             }
28103             Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');
28104         }
28105         Ext.ProgressBar.superclass.onDestroy.call(this);
28106     }
28107 });
28108 Ext.reg('progress', Ext.ProgressBar);/*
28109  * These classes are derivatives of the similarly named classes in the YUI Library.
28110  * The original license:
28111  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
28112  * Code licensed under the BSD License:
28113  * http://developer.yahoo.net/yui/license.txt
28114  */
28115
28116 (function() {
28117
28118 var Event=Ext.EventManager;
28119 var Dom=Ext.lib.Dom;
28120
28121 /**
28122  * @class Ext.dd.DragDrop
28123  * Defines the interface and base operation of items that that can be
28124  * dragged or can be drop targets.  It was designed to be extended, overriding
28125  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
28126  * Up to three html elements can be associated with a DragDrop instance:
28127  * <ul>
28128  * <li>linked element: the element that is passed into the constructor.
28129  * This is the element which defines the boundaries for interaction with
28130  * other DragDrop objects.</li>
28131  * <li>handle element(s): The drag operation only occurs if the element that
28132  * was clicked matches a handle element.  By default this is the linked
28133  * element, but there are times that you will want only a portion of the
28134  * linked element to initiate the drag operation, and the setHandleElId()
28135  * method provides a way to define this.</li>
28136  * <li>drag element: this represents the element that would be moved along
28137  * with the cursor during a drag operation.  By default, this is the linked
28138  * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
28139  * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
28140  * </li>
28141  * </ul>
28142  * This class should not be instantiated until the onload event to ensure that
28143  * the associated elements are available.
28144  * The following would define a DragDrop obj that would interact with any
28145  * other DragDrop obj in the "group1" group:
28146  * <pre>
28147  *  dd = new Ext.dd.DragDrop("div1", "group1");
28148  * </pre>
28149  * Since none of the event handlers have been implemented, nothing would
28150  * actually happen if you were to run the code above.  Normally you would
28151  * override this class or one of the default implementations, but you can
28152  * also override the methods you want on an instance of the class...
28153  * <pre>
28154  *  dd.onDragDrop = function(e, id) {
28155  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
28156  *  }
28157  * </pre>
28158  * @constructor
28159  * @param {String} id of the element that is linked to this instance
28160  * @param {String} sGroup the group of related DragDrop objects
28161  * @param {object} config an object containing configurable attributes
28162  *                Valid properties for DragDrop:
28163  *                    padding, isTarget, maintainOffset, primaryButtonOnly
28164  */
28165 Ext.dd.DragDrop = function(id, sGroup, config) {
28166     if(id) {
28167         this.init(id, sGroup, config);
28168     }
28169 };
28170
28171 Ext.dd.DragDrop.prototype = {
28172
28173     /**
28174      * Set to false to enable a DragDrop object to fire drag events while dragging
28175      * over its own Element. Defaults to true - DragDrop objects do not by default
28176      * fire drag events to themselves.
28177      * @property ignoreSelf
28178      * @type Boolean
28179      */
28180
28181     /**
28182      * The id of the element associated with this object.  This is what we
28183      * refer to as the "linked element" because the size and position of
28184      * this element is used to determine when the drag and drop objects have
28185      * interacted.
28186      * @property id
28187      * @type String
28188      */
28189     id: null,
28190
28191     /**
28192      * Configuration attributes passed into the constructor
28193      * @property config
28194      * @type object
28195      */
28196     config: null,
28197
28198     /**
28199      * The id of the element that will be dragged.  By default this is same
28200      * as the linked element, but could be changed to another element. Ex:
28201      * Ext.dd.DDProxy
28202      * @property dragElId
28203      * @type String
28204      * @private
28205      */
28206     dragElId: null,
28207
28208     /**
28209      * The ID of the element that initiates the drag operation.  By default
28210      * this is the linked element, but could be changed to be a child of this
28211      * element.  This lets us do things like only starting the drag when the
28212      * header element within the linked html element is clicked.
28213      * @property handleElId
28214      * @type String
28215      * @private
28216      */
28217     handleElId: null,
28218
28219     /**
28220      * An object who's property names identify HTML tags to be considered invalid as drag handles.
28221      * A non-null property value identifies the tag as invalid. Defaults to the 
28222      * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
28223 {
28224     A: "A"
28225 }</code></pre>
28226      * @property invalidHandleTypes
28227      * @type Object
28228      */
28229     invalidHandleTypes: null,
28230
28231     /**
28232      * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
28233      * A non-null property value identifies the ID as invalid. For example, to prevent
28234      * dragging from being initiated on element ID "foo", use:<pre><code>
28235 {
28236     foo: true
28237 }</code></pre>
28238      * @property invalidHandleIds
28239      * @type Object
28240      */
28241     invalidHandleIds: null,
28242
28243     /**
28244      * An Array of CSS class names for elements to be considered in valid as drag handles.
28245      * @property invalidHandleClasses
28246      * @type Array
28247      */
28248     invalidHandleClasses: null,
28249
28250     /**
28251      * The linked element's absolute X position at the time the drag was
28252      * started
28253      * @property startPageX
28254      * @type int
28255      * @private
28256      */
28257     startPageX: 0,
28258
28259     /**
28260      * The linked element's absolute X position at the time the drag was
28261      * started
28262      * @property startPageY
28263      * @type int
28264      * @private
28265      */
28266     startPageY: 0,
28267
28268     /**
28269      * The group defines a logical collection of DragDrop objects that are
28270      * related.  Instances only get events when interacting with other
28271      * DragDrop object in the same group.  This lets us define multiple
28272      * groups using a single DragDrop subclass if we want.
28273      * @property groups
28274      * @type object An object in the format {'group1':true, 'group2':true}
28275      */
28276     groups: null,
28277
28278     /**
28279      * Individual drag/drop instances can be locked.  This will prevent
28280      * onmousedown start drag.
28281      * @property locked
28282      * @type boolean
28283      * @private
28284      */
28285     locked: false,
28286
28287     /**
28288      * Lock this instance
28289      * @method lock
28290      */
28291     lock: function() {
28292         this.locked = true;
28293     },
28294
28295     /**
28296      * When set to true, other DD objects in cooperating DDGroups do not receive
28297      * notification events when this DD object is dragged over them. Defaults to false.
28298      * @property moveOnly
28299      * @type boolean
28300      */
28301     moveOnly: false,
28302
28303     /**
28304      * Unlock this instace
28305      * @method unlock
28306      */
28307     unlock: function() {
28308         this.locked = false;
28309     },
28310
28311     /**
28312      * By default, all instances can be a drop target.  This can be disabled by
28313      * setting isTarget to false.
28314      * @property isTarget
28315      * @type boolean
28316      */
28317     isTarget: true,
28318
28319     /**
28320      * The padding configured for this drag and drop object for calculating
28321      * the drop zone intersection with this object.
28322      * @property padding
28323      * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
28324      */
28325     padding: null,
28326
28327     /**
28328      * Cached reference to the linked element
28329      * @property _domRef
28330      * @private
28331      */
28332     _domRef: null,
28333
28334     /**
28335      * Internal typeof flag
28336      * @property __ygDragDrop
28337      * @private
28338      */
28339     __ygDragDrop: true,
28340
28341     /**
28342      * Set to true when horizontal contraints are applied
28343      * @property constrainX
28344      * @type boolean
28345      * @private
28346      */
28347     constrainX: false,
28348
28349     /**
28350      * Set to true when vertical contraints are applied
28351      * @property constrainY
28352      * @type boolean
28353      * @private
28354      */
28355     constrainY: false,
28356
28357     /**
28358      * The left constraint
28359      * @property minX
28360      * @type int
28361      * @private
28362      */
28363     minX: 0,
28364
28365     /**
28366      * The right constraint
28367      * @property maxX
28368      * @type int
28369      * @private
28370      */
28371     maxX: 0,
28372
28373     /**
28374      * The up constraint
28375      * @property minY
28376      * @type int
28377      * @private
28378      */
28379     minY: 0,
28380
28381     /**
28382      * The down constraint
28383      * @property maxY
28384      * @type int
28385      * @private
28386      */
28387     maxY: 0,
28388
28389     /**
28390      * Maintain offsets when we resetconstraints.  Set to true when you want
28391      * the position of the element relative to its parent to stay the same
28392      * when the page changes
28393      *
28394      * @property maintainOffset
28395      * @type boolean
28396      */
28397     maintainOffset: false,
28398
28399     /**
28400      * Array of pixel locations the element will snap to if we specified a
28401      * horizontal graduation/interval.  This array is generated automatically
28402      * when you define a tick interval.
28403      * @property xTicks
28404      * @type int[]
28405      */
28406     xTicks: null,
28407
28408     /**
28409      * Array of pixel locations the element will snap to if we specified a
28410      * vertical graduation/interval.  This array is generated automatically
28411      * when you define a tick interval.
28412      * @property yTicks
28413      * @type int[]
28414      */
28415     yTicks: null,
28416
28417     /**
28418      * By default the drag and drop instance will only respond to the primary
28419      * button click (left button for a right-handed mouse).  Set to true to
28420      * allow drag and drop to start with any mouse click that is propogated
28421      * by the browser
28422      * @property primaryButtonOnly
28423      * @type boolean
28424      */
28425     primaryButtonOnly: true,
28426
28427     /**
28428      * The available property is false until the linked dom element is accessible.
28429      * @property available
28430      * @type boolean
28431      */
28432     available: false,
28433
28434     /**
28435      * By default, drags can only be initiated if the mousedown occurs in the
28436      * region the linked element is.  This is done in part to work around a
28437      * bug in some browsers that mis-report the mousedown if the previous
28438      * mouseup happened outside of the window.  This property is set to true
28439      * if outer handles are defined.
28440      *
28441      * @property hasOuterHandles
28442      * @type boolean
28443      * @default false
28444      */
28445     hasOuterHandles: false,
28446
28447     /**
28448      * Code that executes immediately before the startDrag event
28449      * @method b4StartDrag
28450      * @private
28451      */
28452     b4StartDrag: function(x, y) { },
28453
28454     /**
28455      * Abstract method called after a drag/drop object is clicked
28456      * and the drag or mousedown time thresholds have beeen met.
28457      * @method startDrag
28458      * @param {int} X click location
28459      * @param {int} Y click location
28460      */
28461     startDrag: function(x, y) { /* override this */ },
28462
28463     /**
28464      * Code that executes immediately before the onDrag event
28465      * @method b4Drag
28466      * @private
28467      */
28468     b4Drag: function(e) { },
28469
28470     /**
28471      * Abstract method called during the onMouseMove event while dragging an
28472      * object.
28473      * @method onDrag
28474      * @param {Event} e the mousemove event
28475      */
28476     onDrag: function(e) { /* override this */ },
28477
28478     /**
28479      * Abstract method called when this element fist begins hovering over
28480      * another DragDrop obj
28481      * @method onDragEnter
28482      * @param {Event} e the mousemove event
28483      * @param {String|DragDrop[]} id In POINT mode, the element
28484      * id this is hovering over.  In INTERSECT mode, an array of one or more
28485      * dragdrop items being hovered over.
28486      */
28487     onDragEnter: function(e, id) { /* override this */ },
28488
28489     /**
28490      * Code that executes immediately before the onDragOver event
28491      * @method b4DragOver
28492      * @private
28493      */
28494     b4DragOver: function(e) { },
28495
28496     /**
28497      * Abstract method called when this element is hovering over another
28498      * DragDrop obj
28499      * @method onDragOver
28500      * @param {Event} e the mousemove event
28501      * @param {String|DragDrop[]} id In POINT mode, the element
28502      * id this is hovering over.  In INTERSECT mode, an array of dd items
28503      * being hovered over.
28504      */
28505     onDragOver: function(e, id) { /* override this */ },
28506
28507     /**
28508      * Code that executes immediately before the onDragOut event
28509      * @method b4DragOut
28510      * @private
28511      */
28512     b4DragOut: function(e) { },
28513
28514     /**
28515      * Abstract method called when we are no longer hovering over an element
28516      * @method onDragOut
28517      * @param {Event} e the mousemove event
28518      * @param {String|DragDrop[]} id In POINT mode, the element
28519      * id this was hovering over.  In INTERSECT mode, an array of dd items
28520      * that the mouse is no longer over.
28521      */
28522     onDragOut: function(e, id) { /* override this */ },
28523
28524     /**
28525      * Code that executes immediately before the onDragDrop event
28526      * @method b4DragDrop
28527      * @private
28528      */
28529     b4DragDrop: function(e) { },
28530
28531     /**
28532      * Abstract method called when this item is dropped on another DragDrop
28533      * obj
28534      * @method onDragDrop
28535      * @param {Event} e the mouseup event
28536      * @param {String|DragDrop[]} id In POINT mode, the element
28537      * id this was dropped on.  In INTERSECT mode, an array of dd items this
28538      * was dropped on.
28539      */
28540     onDragDrop: function(e, id) { /* override this */ },
28541
28542     /**
28543      * Abstract method called when this item is dropped on an area with no
28544      * drop target
28545      * @method onInvalidDrop
28546      * @param {Event} e the mouseup event
28547      */
28548     onInvalidDrop: function(e) { /* override this */ },
28549
28550     /**
28551      * Code that executes immediately before the endDrag event
28552      * @method b4EndDrag
28553      * @private
28554      */
28555     b4EndDrag: function(e) { },
28556
28557     /**
28558      * Fired when we are done dragging the object
28559      * @method endDrag
28560      * @param {Event} e the mouseup event
28561      */
28562     endDrag: function(e) { /* override this */ },
28563
28564     /**
28565      * Code executed immediately before the onMouseDown event
28566      * @method b4MouseDown
28567      * @param {Event} e the mousedown event
28568      * @private
28569      */
28570     b4MouseDown: function(e) {  },
28571
28572     /**
28573      * Event handler that fires when a drag/drop obj gets a mousedown
28574      * @method onMouseDown
28575      * @param {Event} e the mousedown event
28576      */
28577     onMouseDown: function(e) { /* override this */ },
28578
28579     /**
28580      * Event handler that fires when a drag/drop obj gets a mouseup
28581      * @method onMouseUp
28582      * @param {Event} e the mouseup event
28583      */
28584     onMouseUp: function(e) { /* override this */ },
28585
28586     /**
28587      * Override the onAvailable method to do what is needed after the initial
28588      * position was determined.
28589      * @method onAvailable
28590      */
28591     onAvailable: function () {
28592     },
28593
28594     /**
28595      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
28596      * @type Object
28597      */
28598     defaultPadding : {left:0, right:0, top:0, bottom:0},
28599
28600     /**
28601      * Initializes the drag drop object's constraints to restrict movement to a certain element.
28602  *
28603  * Usage:
28604  <pre><code>
28605  var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
28606                 { dragElId: "existingProxyDiv" });
28607  dd.startDrag = function(){
28608      this.constrainTo("parent-id");
28609  };
28610  </code></pre>
28611  * Or you can initalize it using the {@link Ext.Element} object:
28612  <pre><code>
28613  Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
28614      startDrag : function(){
28615          this.constrainTo("parent-id");
28616      }
28617  });
28618  </code></pre>
28619      * @param {Mixed} constrainTo The element to constrain to.
28620      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
28621      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
28622      * an object containing the sides to pad. For example: {right:10, bottom:10}
28623      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
28624      */
28625     constrainTo : function(constrainTo, pad, inContent){
28626         if(Ext.isNumber(pad)){
28627             pad = {left: pad, right:pad, top:pad, bottom:pad};
28628         }
28629         pad = pad || this.defaultPadding;
28630         var b = Ext.get(this.getEl()).getBox(),
28631             ce = Ext.get(constrainTo),
28632             s = ce.getScroll(),
28633             c, 
28634             cd = ce.dom;
28635         if(cd == document.body){
28636             c = { x: s.left, y: s.top, width: Ext.lib.Dom.getViewWidth(), height: Ext.lib.Dom.getViewHeight()};
28637         }else{
28638             var xy = ce.getXY();
28639             c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
28640         }
28641
28642
28643         var topSpace = b.y - c.y,
28644             leftSpace = b.x - c.x;
28645
28646         this.resetConstraints();
28647         this.setXConstraint(leftSpace - (pad.left||0), // left
28648                 c.width - leftSpace - b.width - (pad.right||0), //right
28649                                 this.xTickSize
28650         );
28651         this.setYConstraint(topSpace - (pad.top||0), //top
28652                 c.height - topSpace - b.height - (pad.bottom||0), //bottom
28653                                 this.yTickSize
28654         );
28655     },
28656
28657     /**
28658      * Returns a reference to the linked element
28659      * @method getEl
28660      * @return {HTMLElement} the html element
28661      */
28662     getEl: function() {
28663         if (!this._domRef) {
28664             this._domRef = Ext.getDom(this.id);
28665         }
28666
28667         return this._domRef;
28668     },
28669
28670     /**
28671      * Returns a reference to the actual element to drag.  By default this is
28672      * the same as the html element, but it can be assigned to another
28673      * element. An example of this can be found in Ext.dd.DDProxy
28674      * @method getDragEl
28675      * @return {HTMLElement} the html element
28676      */
28677     getDragEl: function() {
28678         return Ext.getDom(this.dragElId);
28679     },
28680
28681     /**
28682      * Sets up the DragDrop object.  Must be called in the constructor of any
28683      * Ext.dd.DragDrop subclass
28684      * @method init
28685      * @param id the id of the linked element
28686      * @param {String} sGroup the group of related items
28687      * @param {object} config configuration attributes
28688      */
28689     init: function(id, sGroup, config) {
28690         this.initTarget(id, sGroup, config);
28691         Event.on(this.id, "mousedown", this.handleMouseDown, this);
28692         // Event.on(this.id, "selectstart", Event.preventDefault);
28693     },
28694
28695     /**
28696      * Initializes Targeting functionality only... the object does not
28697      * get a mousedown handler.
28698      * @method initTarget
28699      * @param id the id of the linked element
28700      * @param {String} sGroup the group of related items
28701      * @param {object} config configuration attributes
28702      */
28703     initTarget: function(id, sGroup, config) {
28704
28705         // configuration attributes
28706         this.config = config || {};
28707
28708         // create a local reference to the drag and drop manager
28709         this.DDM = Ext.dd.DDM;
28710         // initialize the groups array
28711         this.groups = {};
28712
28713         // assume that we have an element reference instead of an id if the
28714         // parameter is not a string
28715         if (typeof id !== "string") {
28716             id = Ext.id(id);
28717         }
28718
28719         // set the id
28720         this.id = id;
28721
28722         // add to an interaction group
28723         this.addToGroup((sGroup) ? sGroup : "default");
28724
28725         // We don't want to register this as the handle with the manager
28726         // so we just set the id rather than calling the setter.
28727         this.handleElId = id;
28728
28729         // the linked element is the element that gets dragged by default
28730         this.setDragElId(id);
28731
28732         // by default, clicked anchors will not start drag operations.
28733         this.invalidHandleTypes = { A: "A" };
28734         this.invalidHandleIds = {};
28735         this.invalidHandleClasses = [];
28736
28737         this.applyConfig();
28738
28739         this.handleOnAvailable();
28740     },
28741
28742     /**
28743      * Applies the configuration parameters that were passed into the constructor.
28744      * This is supposed to happen at each level through the inheritance chain.  So
28745      * a DDProxy implentation will execute apply config on DDProxy, DD, and
28746      * DragDrop in order to get all of the parameters that are available in
28747      * each object.
28748      * @method applyConfig
28749      */
28750     applyConfig: function() {
28751
28752         // configurable properties:
28753         //    padding, isTarget, maintainOffset, primaryButtonOnly
28754         this.padding           = this.config.padding || [0, 0, 0, 0];
28755         this.isTarget          = (this.config.isTarget !== false);
28756         this.maintainOffset    = (this.config.maintainOffset);
28757         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
28758
28759     },
28760
28761     /**
28762      * Executed when the linked element is available
28763      * @method handleOnAvailable
28764      * @private
28765      */
28766     handleOnAvailable: function() {
28767         this.available = true;
28768         this.resetConstraints();
28769         this.onAvailable();
28770     },
28771
28772      /**
28773      * Configures the padding for the target zone in px.  Effectively expands
28774      * (or reduces) the virtual object size for targeting calculations.
28775      * Supports css-style shorthand; if only one parameter is passed, all sides
28776      * will have that padding, and if only two are passed, the top and bottom
28777      * will have the first param, the left and right the second.
28778      * @method setPadding
28779      * @param {int} iTop    Top pad
28780      * @param {int} iRight  Right pad
28781      * @param {int} iBot    Bot pad
28782      * @param {int} iLeft   Left pad
28783      */
28784     setPadding: function(iTop, iRight, iBot, iLeft) {
28785         // this.padding = [iLeft, iRight, iTop, iBot];
28786         if (!iRight && 0 !== iRight) {
28787             this.padding = [iTop, iTop, iTop, iTop];
28788         } else if (!iBot && 0 !== iBot) {
28789             this.padding = [iTop, iRight, iTop, iRight];
28790         } else {
28791             this.padding = [iTop, iRight, iBot, iLeft];
28792         }
28793     },
28794
28795     /**
28796      * Stores the initial placement of the linked element.
28797      * @method setInitPosition
28798      * @param {int} diffX   the X offset, default 0
28799      * @param {int} diffY   the Y offset, default 0
28800      */
28801     setInitPosition: function(diffX, diffY) {
28802         var el = this.getEl();
28803
28804         if (!this.DDM.verifyEl(el)) {
28805             return;
28806         }
28807
28808         var dx = diffX || 0;
28809         var dy = diffY || 0;
28810
28811         var p = Dom.getXY( el );
28812
28813         this.initPageX = p[0] - dx;
28814         this.initPageY = p[1] - dy;
28815
28816         this.lastPageX = p[0];
28817         this.lastPageY = p[1];
28818
28819         this.setStartPosition(p);
28820     },
28821
28822     /**
28823      * Sets the start position of the element.  This is set when the obj
28824      * is initialized, the reset when a drag is started.
28825      * @method setStartPosition
28826      * @param pos current position (from previous lookup)
28827      * @private
28828      */
28829     setStartPosition: function(pos) {
28830         var p = pos || Dom.getXY( this.getEl() );
28831         this.deltaSetXY = null;
28832
28833         this.startPageX = p[0];
28834         this.startPageY = p[1];
28835     },
28836
28837     /**
28838      * Add this instance to a group of related drag/drop objects.  All
28839      * instances belong to at least one group, and can belong to as many
28840      * groups as needed.
28841      * @method addToGroup
28842      * @param sGroup {string} the name of the group
28843      */
28844     addToGroup: function(sGroup) {
28845         this.groups[sGroup] = true;
28846         this.DDM.regDragDrop(this, sGroup);
28847     },
28848
28849     /**
28850      * Remove's this instance from the supplied interaction group
28851      * @method removeFromGroup
28852      * @param {string}  sGroup  The group to drop
28853      */
28854     removeFromGroup: function(sGroup) {
28855         if (this.groups[sGroup]) {
28856             delete this.groups[sGroup];
28857         }
28858
28859         this.DDM.removeDDFromGroup(this, sGroup);
28860     },
28861
28862     /**
28863      * Allows you to specify that an element other than the linked element
28864      * will be moved with the cursor during a drag
28865      * @method setDragElId
28866      * @param id {string} the id of the element that will be used to initiate the drag
28867      */
28868     setDragElId: function(id) {
28869         this.dragElId = id;
28870     },
28871
28872     /**
28873      * Allows you to specify a child of the linked element that should be
28874      * used to initiate the drag operation.  An example of this would be if
28875      * you have a content div with text and links.  Clicking anywhere in the
28876      * content area would normally start the drag operation.  Use this method
28877      * to specify that an element inside of the content div is the element
28878      * that starts the drag operation.
28879      * @method setHandleElId
28880      * @param id {string} the id of the element that will be used to
28881      * initiate the drag.
28882      */
28883     setHandleElId: function(id) {
28884         if (typeof id !== "string") {
28885             id = Ext.id(id);
28886         }
28887         this.handleElId = id;
28888         this.DDM.regHandle(this.id, id);
28889     },
28890
28891     /**
28892      * Allows you to set an element outside of the linked element as a drag
28893      * handle
28894      * @method setOuterHandleElId
28895      * @param id the id of the element that will be used to initiate the drag
28896      */
28897     setOuterHandleElId: function(id) {
28898         if (typeof id !== "string") {
28899             id = Ext.id(id);
28900         }
28901         Event.on(id, "mousedown",
28902                 this.handleMouseDown, this);
28903         this.setHandleElId(id);
28904
28905         this.hasOuterHandles = true;
28906     },
28907
28908     /**
28909      * Remove all drag and drop hooks for this element
28910      * @method unreg
28911      */
28912     unreg: function() {
28913         Event.un(this.id, "mousedown",
28914                 this.handleMouseDown);
28915         this._domRef = null;
28916         this.DDM._remove(this);
28917     },
28918
28919     destroy : function(){
28920         this.unreg();
28921     },
28922
28923     /**
28924      * Returns true if this instance is locked, or the drag drop mgr is locked
28925      * (meaning that all drag/drop is disabled on the page.)
28926      * @method isLocked
28927      * @return {boolean} true if this obj or all drag/drop is locked, else
28928      * false
28929      */
28930     isLocked: function() {
28931         return (this.DDM.isLocked() || this.locked);
28932     },
28933
28934     /**
28935      * Fired when this object is clicked
28936      * @method handleMouseDown
28937      * @param {Event} e
28938      * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
28939      * @private
28940      */
28941     handleMouseDown: function(e, oDD){
28942         if (this.primaryButtonOnly && e.button != 0) {
28943             return;
28944         }
28945
28946         if (this.isLocked()) {
28947             return;
28948         }
28949
28950         this.DDM.refreshCache(this.groups);
28951
28952         var pt = new Ext.lib.Point(Ext.lib.Event.getPageX(e), Ext.lib.Event.getPageY(e));
28953         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
28954         } else {
28955             if (this.clickValidator(e)) {
28956
28957                 // set the initial element position
28958                 this.setStartPosition();
28959
28960                 this.b4MouseDown(e);
28961                 this.onMouseDown(e);
28962
28963                 this.DDM.handleMouseDown(e, this);
28964
28965                 this.DDM.stopEvent(e);
28966             } else {
28967
28968
28969             }
28970         }
28971     },
28972
28973     clickValidator: function(e) {
28974         var target = e.getTarget();
28975         return ( this.isValidHandleChild(target) &&
28976                     (this.id == this.handleElId ||
28977                         this.DDM.handleWasClicked(target, this.id)) );
28978     },
28979
28980     /**
28981      * Allows you to specify a tag name that should not start a drag operation
28982      * when clicked.  This is designed to facilitate embedding links within a
28983      * drag handle that do something other than start the drag.
28984      * @method addInvalidHandleType
28985      * @param {string} tagName the type of element to exclude
28986      */
28987     addInvalidHandleType: function(tagName) {
28988         var type = tagName.toUpperCase();
28989         this.invalidHandleTypes[type] = type;
28990     },
28991
28992     /**
28993      * Lets you to specify an element id for a child of a drag handle
28994      * that should not initiate a drag
28995      * @method addInvalidHandleId
28996      * @param {string} id the element id of the element you wish to ignore
28997      */
28998     addInvalidHandleId: function(id) {
28999         if (typeof id !== "string") {
29000             id = Ext.id(id);
29001         }
29002         this.invalidHandleIds[id] = id;
29003     },
29004
29005     /**
29006      * Lets you specify a css class of elements that will not initiate a drag
29007      * @method addInvalidHandleClass
29008      * @param {string} cssClass the class of the elements you wish to ignore
29009      */
29010     addInvalidHandleClass: function(cssClass) {
29011         this.invalidHandleClasses.push(cssClass);
29012     },
29013
29014     /**
29015      * Unsets an excluded tag name set by addInvalidHandleType
29016      * @method removeInvalidHandleType
29017      * @param {string} tagName the type of element to unexclude
29018      */
29019     removeInvalidHandleType: function(tagName) {
29020         var type = tagName.toUpperCase();
29021         // this.invalidHandleTypes[type] = null;
29022         delete this.invalidHandleTypes[type];
29023     },
29024
29025     /**
29026      * Unsets an invalid handle id
29027      * @method removeInvalidHandleId
29028      * @param {string} id the id of the element to re-enable
29029      */
29030     removeInvalidHandleId: function(id) {
29031         if (typeof id !== "string") {
29032             id = Ext.id(id);
29033         }
29034         delete this.invalidHandleIds[id];
29035     },
29036
29037     /**
29038      * Unsets an invalid css class
29039      * @method removeInvalidHandleClass
29040      * @param {string} cssClass the class of the element(s) you wish to
29041      * re-enable
29042      */
29043     removeInvalidHandleClass: function(cssClass) {
29044         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
29045             if (this.invalidHandleClasses[i] == cssClass) {
29046                 delete this.invalidHandleClasses[i];
29047             }
29048         }
29049     },
29050
29051     /**
29052      * Checks the tag exclusion list to see if this click should be ignored
29053      * @method isValidHandleChild
29054      * @param {HTMLElement} node the HTMLElement to evaluate
29055      * @return {boolean} true if this is a valid tag type, false if not
29056      */
29057     isValidHandleChild: function(node) {
29058
29059         var valid = true;
29060         // var n = (node.nodeName == "#text") ? node.parentNode : node;
29061         var nodeName;
29062         try {
29063             nodeName = node.nodeName.toUpperCase();
29064         } catch(e) {
29065             nodeName = node.nodeName;
29066         }
29067         valid = valid && !this.invalidHandleTypes[nodeName];
29068         valid = valid && !this.invalidHandleIds[node.id];
29069
29070         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
29071             valid = !Ext.fly(node).hasClass(this.invalidHandleClasses[i]);
29072         }
29073
29074
29075         return valid;
29076
29077     },
29078
29079     /**
29080      * Create the array of horizontal tick marks if an interval was specified
29081      * in setXConstraint().
29082      * @method setXTicks
29083      * @private
29084      */
29085     setXTicks: function(iStartX, iTickSize) {
29086         this.xTicks = [];
29087         this.xTickSize = iTickSize;
29088
29089         var tickMap = {};
29090
29091         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
29092             if (!tickMap[i]) {
29093                 this.xTicks[this.xTicks.length] = i;
29094                 tickMap[i] = true;
29095             }
29096         }
29097
29098         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
29099             if (!tickMap[i]) {
29100                 this.xTicks[this.xTicks.length] = i;
29101                 tickMap[i] = true;
29102             }
29103         }
29104
29105         this.xTicks.sort(this.DDM.numericSort) ;
29106     },
29107
29108     /**
29109      * Create the array of vertical tick marks if an interval was specified in
29110      * setYConstraint().
29111      * @method setYTicks
29112      * @private
29113      */
29114     setYTicks: function(iStartY, iTickSize) {
29115         this.yTicks = [];
29116         this.yTickSize = iTickSize;
29117
29118         var tickMap = {};
29119
29120         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
29121             if (!tickMap[i]) {
29122                 this.yTicks[this.yTicks.length] = i;
29123                 tickMap[i] = true;
29124             }
29125         }
29126
29127         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
29128             if (!tickMap[i]) {
29129                 this.yTicks[this.yTicks.length] = i;
29130                 tickMap[i] = true;
29131             }
29132         }
29133
29134         this.yTicks.sort(this.DDM.numericSort) ;
29135     },
29136
29137     /**
29138      * By default, the element can be dragged any place on the screen.  Use
29139      * this method to limit the horizontal travel of the element.  Pass in
29140      * 0,0 for the parameters if you want to lock the drag to the y axis.
29141      * @method setXConstraint
29142      * @param {int} iLeft the number of pixels the element can move to the left
29143      * @param {int} iRight the number of pixels the element can move to the
29144      * right
29145      * @param {int} iTickSize optional parameter for specifying that the
29146      * element
29147      * should move iTickSize pixels at a time.
29148      */
29149     setXConstraint: function(iLeft, iRight, iTickSize) {
29150         this.leftConstraint = iLeft;
29151         this.rightConstraint = iRight;
29152
29153         this.minX = this.initPageX - iLeft;
29154         this.maxX = this.initPageX + iRight;
29155         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
29156
29157         this.constrainX = true;
29158     },
29159
29160     /**
29161      * Clears any constraints applied to this instance.  Also clears ticks
29162      * since they can't exist independent of a constraint at this time.
29163      * @method clearConstraints
29164      */
29165     clearConstraints: function() {
29166         this.constrainX = false;
29167         this.constrainY = false;
29168         this.clearTicks();
29169     },
29170
29171     /**
29172      * Clears any tick interval defined for this instance
29173      * @method clearTicks
29174      */
29175     clearTicks: function() {
29176         this.xTicks = null;
29177         this.yTicks = null;
29178         this.xTickSize = 0;
29179         this.yTickSize = 0;
29180     },
29181
29182     /**
29183      * By default, the element can be dragged any place on the screen.  Set
29184      * this to limit the vertical travel of the element.  Pass in 0,0 for the
29185      * parameters if you want to lock the drag to the x axis.
29186      * @method setYConstraint
29187      * @param {int} iUp the number of pixels the element can move up
29188      * @param {int} iDown the number of pixels the element can move down
29189      * @param {int} iTickSize optional parameter for specifying that the
29190      * element should move iTickSize pixels at a time.
29191      */
29192     setYConstraint: function(iUp, iDown, iTickSize) {
29193         this.topConstraint = iUp;
29194         this.bottomConstraint = iDown;
29195
29196         this.minY = this.initPageY - iUp;
29197         this.maxY = this.initPageY + iDown;
29198         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
29199
29200         this.constrainY = true;
29201
29202     },
29203
29204     /**
29205      * resetConstraints must be called if you manually reposition a dd element.
29206      * @method resetConstraints
29207      * @param {boolean} maintainOffset
29208      */
29209     resetConstraints: function() {
29210         // Maintain offsets if necessary
29211         if (this.initPageX || this.initPageX === 0) {
29212             // figure out how much this thing has moved
29213             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
29214             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
29215
29216             this.setInitPosition(dx, dy);
29217
29218         // This is the first time we have detected the element's position
29219         } else {
29220             this.setInitPosition();
29221         }
29222
29223         if (this.constrainX) {
29224             this.setXConstraint( this.leftConstraint,
29225                                  this.rightConstraint,
29226                                  this.xTickSize        );
29227         }
29228
29229         if (this.constrainY) {
29230             this.setYConstraint( this.topConstraint,
29231                                  this.bottomConstraint,
29232                                  this.yTickSize         );
29233         }
29234     },
29235
29236     /**
29237      * Normally the drag element is moved pixel by pixel, but we can specify
29238      * that it move a number of pixels at a time.  This method resolves the
29239      * location when we have it set up like this.
29240      * @method getTick
29241      * @param {int} val where we want to place the object
29242      * @param {int[]} tickArray sorted array of valid points
29243      * @return {int} the closest tick
29244      * @private
29245      */
29246     getTick: function(val, tickArray) {
29247         if (!tickArray) {
29248             // If tick interval is not defined, it is effectively 1 pixel,
29249             // so we return the value passed to us.
29250             return val;
29251         } else if (tickArray[0] >= val) {
29252             // The value is lower than the first tick, so we return the first
29253             // tick.
29254             return tickArray[0];
29255         } else {
29256             for (var i=0, len=tickArray.length; i<len; ++i) {
29257                 var next = i + 1;
29258                 if (tickArray[next] && tickArray[next] >= val) {
29259                     var diff1 = val - tickArray[i];
29260                     var diff2 = tickArray[next] - val;
29261                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
29262                 }
29263             }
29264
29265             // The value is larger than the last tick, so we return the last
29266             // tick.
29267             return tickArray[tickArray.length - 1];
29268         }
29269     },
29270
29271     /**
29272      * toString method
29273      * @method toString
29274      * @return {string} string representation of the dd obj
29275      */
29276     toString: function() {
29277         return ("DragDrop " + this.id);
29278     }
29279
29280 };
29281
29282 })();
29283 /*
29284  * The drag and drop utility provides a framework for building drag and drop
29285  * applications.  In addition to enabling drag and drop for specific elements,
29286  * the drag and drop elements are tracked by the manager class, and the
29287  * interactions between the various elements are tracked during the drag and
29288  * the implementing code is notified about these important moments.
29289  */
29290
29291 // Only load the library once.  Rewriting the manager class would orphan
29292 // existing drag and drop instances.
29293 if (!Ext.dd.DragDropMgr) {
29294
29295 /**
29296  * @class Ext.dd.DragDropMgr
29297  * DragDropMgr is a singleton that tracks the element interaction for
29298  * all DragDrop items in the window.  Generally, you will not call
29299  * this class directly, but it does have helper methods that could
29300  * be useful in your DragDrop implementations.
29301  * @singleton
29302  */
29303 Ext.dd.DragDropMgr = function() {
29304
29305     var Event = Ext.EventManager;
29306
29307     return {
29308
29309         /**
29310          * Two dimensional Array of registered DragDrop objects.  The first
29311          * dimension is the DragDrop item group, the second the DragDrop
29312          * object.
29313          * @property ids
29314          * @type String[]
29315          * @private
29316          * @static
29317          */
29318         ids: {},
29319
29320         /**
29321          * Array of element ids defined as drag handles.  Used to determine
29322          * if the element that generated the mousedown event is actually the
29323          * handle and not the html element itself.
29324          * @property handleIds
29325          * @type String[]
29326          * @private
29327          * @static
29328          */
29329         handleIds: {},
29330
29331         /**
29332          * the DragDrop object that is currently being dragged
29333          * @property dragCurrent
29334          * @type DragDrop
29335          * @private
29336          * @static
29337          **/
29338         dragCurrent: null,
29339
29340         /**
29341          * the DragDrop object(s) that are being hovered over
29342          * @property dragOvers
29343          * @type Array
29344          * @private
29345          * @static
29346          */
29347         dragOvers: {},
29348
29349         /**
29350          * the X distance between the cursor and the object being dragged
29351          * @property deltaX
29352          * @type int
29353          * @private
29354          * @static
29355          */
29356         deltaX: 0,
29357
29358         /**
29359          * the Y distance between the cursor and the object being dragged
29360          * @property deltaY
29361          * @type int
29362          * @private
29363          * @static
29364          */
29365         deltaY: 0,
29366
29367         /**
29368          * Flag to determine if we should prevent the default behavior of the
29369          * events we define. By default this is true, but this can be set to
29370          * false if you need the default behavior (not recommended)
29371          * @property preventDefault
29372          * @type boolean
29373          * @static
29374          */
29375         preventDefault: true,
29376
29377         /**
29378          * Flag to determine if we should stop the propagation of the events
29379          * we generate. This is true by default but you may want to set it to
29380          * false if the html element contains other features that require the
29381          * mouse click.
29382          * @property stopPropagation
29383          * @type boolean
29384          * @static
29385          */
29386         stopPropagation: true,
29387
29388         /**
29389          * Internal flag that is set to true when drag and drop has been
29390          * intialized
29391          * @property initialized
29392          * @private
29393          * @static
29394          */
29395         initialized: false,
29396
29397         /**
29398          * All drag and drop can be disabled.
29399          * @property locked
29400          * @private
29401          * @static
29402          */
29403         locked: false,
29404
29405         /**
29406          * Called the first time an element is registered.
29407          * @method init
29408          * @private
29409          * @static
29410          */
29411         init: function() {
29412             this.initialized = true;
29413         },
29414
29415         /**
29416          * In point mode, drag and drop interaction is defined by the
29417          * location of the cursor during the drag/drop
29418          * @property POINT
29419          * @type int
29420          * @static
29421          */
29422         POINT: 0,
29423
29424         /**
29425          * In intersect mode, drag and drop interaction is defined by the
29426          * overlap of two or more drag and drop objects.
29427          * @property INTERSECT
29428          * @type int
29429          * @static
29430          */
29431         INTERSECT: 1,
29432
29433         /**
29434          * The current drag and drop mode.  Default: POINT
29435          * @property mode
29436          * @type int
29437          * @static
29438          */
29439         mode: 0,
29440
29441         /**
29442          * Runs method on all drag and drop objects
29443          * @method _execOnAll
29444          * @private
29445          * @static
29446          */
29447         _execOnAll: function(sMethod, args) {
29448             for (var i in this.ids) {
29449                 for (var j in this.ids[i]) {
29450                     var oDD = this.ids[i][j];
29451                     if (! this.isTypeOfDD(oDD)) {
29452                         continue;
29453                     }
29454                     oDD[sMethod].apply(oDD, args);
29455                 }
29456             }
29457         },
29458
29459         /**
29460          * Drag and drop initialization.  Sets up the global event handlers
29461          * @method _onLoad
29462          * @private
29463          * @static
29464          */
29465         _onLoad: function() {
29466
29467             this.init();
29468
29469
29470             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
29471             Event.on(document, "mousemove", this.handleMouseMove, this, true);
29472             Event.on(window,   "unload",    this._onUnload, this, true);
29473             Event.on(window,   "resize",    this._onResize, this, true);
29474             // Event.on(window,   "mouseout",    this._test);
29475
29476         },
29477
29478         /**
29479          * Reset constraints on all drag and drop objs
29480          * @method _onResize
29481          * @private
29482          * @static
29483          */
29484         _onResize: function(e) {
29485             this._execOnAll("resetConstraints", []);
29486         },
29487
29488         /**
29489          * Lock all drag and drop functionality
29490          * @method lock
29491          * @static
29492          */
29493         lock: function() { this.locked = true; },
29494
29495         /**
29496          * Unlock all drag and drop functionality
29497          * @method unlock
29498          * @static
29499          */
29500         unlock: function() { this.locked = false; },
29501
29502         /**
29503          * Is drag and drop locked?
29504          * @method isLocked
29505          * @return {boolean} True if drag and drop is locked, false otherwise.
29506          * @static
29507          */
29508         isLocked: function() { return this.locked; },
29509
29510         /**
29511          * Location cache that is set for all drag drop objects when a drag is
29512          * initiated, cleared when the drag is finished.
29513          * @property locationCache
29514          * @private
29515          * @static
29516          */
29517         locationCache: {},
29518
29519         /**
29520          * Set useCache to false if you want to force object the lookup of each
29521          * drag and drop linked element constantly during a drag.
29522          * @property useCache
29523          * @type boolean
29524          * @static
29525          */
29526         useCache: true,
29527
29528         /**
29529          * The number of pixels that the mouse needs to move after the
29530          * mousedown before the drag is initiated.  Default=3;
29531          * @property clickPixelThresh
29532          * @type int
29533          * @static
29534          */
29535         clickPixelThresh: 3,
29536
29537         /**
29538          * The number of milliseconds after the mousedown event to initiate the
29539          * drag if we don't get a mouseup event. Default=350
29540          * @property clickTimeThresh
29541          * @type int
29542          * @static
29543          */
29544         clickTimeThresh: 350,
29545
29546         /**
29547          * Flag that indicates that either the drag pixel threshold or the
29548          * mousdown time threshold has been met
29549          * @property dragThreshMet
29550          * @type boolean
29551          * @private
29552          * @static
29553          */
29554         dragThreshMet: false,
29555
29556         /**
29557          * Timeout used for the click time threshold
29558          * @property clickTimeout
29559          * @type Object
29560          * @private
29561          * @static
29562          */
29563         clickTimeout: null,
29564
29565         /**
29566          * The X position of the mousedown event stored for later use when a
29567          * drag threshold is met.
29568          * @property startX
29569          * @type int
29570          * @private
29571          * @static
29572          */
29573         startX: 0,
29574
29575         /**
29576          * The Y position of the mousedown event stored for later use when a
29577          * drag threshold is met.
29578          * @property startY
29579          * @type int
29580          * @private
29581          * @static
29582          */
29583         startY: 0,
29584
29585         /**
29586          * Each DragDrop instance must be registered with the DragDropMgr.
29587          * This is executed in DragDrop.init()
29588          * @method regDragDrop
29589          * @param {DragDrop} oDD the DragDrop object to register
29590          * @param {String} sGroup the name of the group this element belongs to
29591          * @static
29592          */
29593         regDragDrop: function(oDD, sGroup) {
29594             if (!this.initialized) { this.init(); }
29595
29596             if (!this.ids[sGroup]) {
29597                 this.ids[sGroup] = {};
29598             }
29599             this.ids[sGroup][oDD.id] = oDD;
29600         },
29601
29602         /**
29603          * Removes the supplied dd instance from the supplied group. Executed
29604          * by DragDrop.removeFromGroup, so don't call this function directly.
29605          * @method removeDDFromGroup
29606          * @private
29607          * @static
29608          */
29609         removeDDFromGroup: function(oDD, sGroup) {
29610             if (!this.ids[sGroup]) {
29611                 this.ids[sGroup] = {};
29612             }
29613
29614             var obj = this.ids[sGroup];
29615             if (obj && obj[oDD.id]) {
29616                 delete obj[oDD.id];
29617             }
29618         },
29619
29620         /**
29621          * Unregisters a drag and drop item.  This is executed in
29622          * DragDrop.unreg, use that method instead of calling this directly.
29623          * @method _remove
29624          * @private
29625          * @static
29626          */
29627         _remove: function(oDD) {
29628             for (var g in oDD.groups) {
29629                 if (g && this.ids[g] && this.ids[g][oDD.id]) {
29630                     delete this.ids[g][oDD.id];
29631                 }
29632             }
29633             delete this.handleIds[oDD.id];
29634         },
29635
29636         /**
29637          * Each DragDrop handle element must be registered.  This is done
29638          * automatically when executing DragDrop.setHandleElId()
29639          * @method regHandle
29640          * @param {String} sDDId the DragDrop id this element is a handle for
29641          * @param {String} sHandleId the id of the element that is the drag
29642          * handle
29643          * @static
29644          */
29645         regHandle: function(sDDId, sHandleId) {
29646             if (!this.handleIds[sDDId]) {
29647                 this.handleIds[sDDId] = {};
29648             }
29649             this.handleIds[sDDId][sHandleId] = sHandleId;
29650         },
29651
29652         /**
29653          * Utility function to determine if a given element has been
29654          * registered as a drag drop item.
29655          * @method isDragDrop
29656          * @param {String} id the element id to check
29657          * @return {boolean} true if this element is a DragDrop item,
29658          * false otherwise
29659          * @static
29660          */
29661         isDragDrop: function(id) {
29662             return ( this.getDDById(id) ) ? true : false;
29663         },
29664
29665         /**
29666          * Returns the drag and drop instances that are in all groups the
29667          * passed in instance belongs to.
29668          * @method getRelated
29669          * @param {DragDrop} p_oDD the obj to get related data for
29670          * @param {boolean} bTargetsOnly if true, only return targetable objs
29671          * @return {DragDrop[]} the related instances
29672          * @static
29673          */
29674         getRelated: function(p_oDD, bTargetsOnly) {
29675             var oDDs = [];
29676             for (var i in p_oDD.groups) {
29677                 for (var j in this.ids[i]) {
29678                     var dd = this.ids[i][j];
29679                     if (! this.isTypeOfDD(dd)) {
29680                         continue;
29681                     }
29682                     if (!bTargetsOnly || dd.isTarget) {
29683                         oDDs[oDDs.length] = dd;
29684                     }
29685                 }
29686             }
29687
29688             return oDDs;
29689         },
29690
29691         /**
29692          * Returns true if the specified dd target is a legal target for
29693          * the specifice drag obj
29694          * @method isLegalTarget
29695          * @param {DragDrop} oDD the drag obj
29696          * @param {DragDrop} oTargetDD the target
29697          * @return {boolean} true if the target is a legal target for the
29698          * dd obj
29699          * @static
29700          */
29701         isLegalTarget: function (oDD, oTargetDD) {
29702             var targets = this.getRelated(oDD, true);
29703             for (var i=0, len=targets.length;i<len;++i) {
29704                 if (targets[i].id == oTargetDD.id) {
29705                     return true;
29706                 }
29707             }
29708
29709             return false;
29710         },
29711
29712         /**
29713          * My goal is to be able to transparently determine if an object is
29714          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
29715          * returns "object", oDD.constructor.toString() always returns
29716          * "DragDrop" and not the name of the subclass.  So for now it just
29717          * evaluates a well-known variable in DragDrop.
29718          * @method isTypeOfDD
29719          * @param {Object} the object to evaluate
29720          * @return {boolean} true if typeof oDD = DragDrop
29721          * @static
29722          */
29723         isTypeOfDD: function (oDD) {
29724             return (oDD && oDD.__ygDragDrop);
29725         },
29726
29727         /**
29728          * Utility function to determine if a given element has been
29729          * registered as a drag drop handle for the given Drag Drop object.
29730          * @method isHandle
29731          * @param {String} id the element id to check
29732          * @return {boolean} true if this element is a DragDrop handle, false
29733          * otherwise
29734          * @static
29735          */
29736         isHandle: function(sDDId, sHandleId) {
29737             return ( this.handleIds[sDDId] &&
29738                             this.handleIds[sDDId][sHandleId] );
29739         },
29740
29741         /**
29742          * Returns the DragDrop instance for a given id
29743          * @method getDDById
29744          * @param {String} id the id of the DragDrop object
29745          * @return {DragDrop} the drag drop object, null if it is not found
29746          * @static
29747          */
29748         getDDById: function(id) {
29749             for (var i in this.ids) {
29750                 if (this.ids[i][id]) {
29751                     return this.ids[i][id];
29752                 }
29753             }
29754             return null;
29755         },
29756
29757         /**
29758          * Fired after a registered DragDrop object gets the mousedown event.
29759          * Sets up the events required to track the object being dragged
29760          * @method handleMouseDown
29761          * @param {Event} e the event
29762          * @param oDD the DragDrop object being dragged
29763          * @private
29764          * @static
29765          */
29766         handleMouseDown: function(e, oDD) {
29767             if(Ext.QuickTips){
29768                 Ext.QuickTips.disable();
29769             }
29770             if(this.dragCurrent){
29771                 // the original browser mouseup wasn't handled (e.g. outside FF browser window)
29772                 // so clean up first to avoid breaking the next drag
29773                 this.handleMouseUp(e);
29774             }
29775             
29776             this.currentTarget = e.getTarget();
29777             this.dragCurrent = oDD;
29778
29779             var el = oDD.getEl();
29780
29781             // track start position
29782             this.startX = e.getPageX();
29783             this.startY = e.getPageY();
29784
29785             this.deltaX = this.startX - el.offsetLeft;
29786             this.deltaY = this.startY - el.offsetTop;
29787
29788             this.dragThreshMet = false;
29789
29790             this.clickTimeout = setTimeout(
29791                     function() {
29792                         var DDM = Ext.dd.DDM;
29793                         DDM.startDrag(DDM.startX, DDM.startY);
29794                     },
29795                     this.clickTimeThresh );
29796         },
29797
29798         /**
29799          * Fired when either the drag pixel threshol or the mousedown hold
29800          * time threshold has been met.
29801          * @method startDrag
29802          * @param x {int} the X position of the original mousedown
29803          * @param y {int} the Y position of the original mousedown
29804          * @static
29805          */
29806         startDrag: function(x, y) {
29807             clearTimeout(this.clickTimeout);
29808             if (this.dragCurrent) {
29809                 this.dragCurrent.b4StartDrag(x, y);
29810                 this.dragCurrent.startDrag(x, y);
29811             }
29812             this.dragThreshMet = true;
29813         },
29814
29815         /**
29816          * Internal function to handle the mouseup event.  Will be invoked
29817          * from the context of the document.
29818          * @method handleMouseUp
29819          * @param {Event} e the event
29820          * @private
29821          * @static
29822          */
29823         handleMouseUp: function(e) {
29824
29825             if(Ext.QuickTips){
29826                 Ext.QuickTips.enable();
29827             }
29828             if (! this.dragCurrent) {
29829                 return;
29830             }
29831
29832             clearTimeout(this.clickTimeout);
29833
29834             if (this.dragThreshMet) {
29835                 this.fireEvents(e, true);
29836             } else {
29837             }
29838
29839             this.stopDrag(e);
29840
29841             this.stopEvent(e);
29842         },
29843
29844         /**
29845          * Utility to stop event propagation and event default, if these
29846          * features are turned on.
29847          * @method stopEvent
29848          * @param {Event} e the event as returned by this.getEvent()
29849          * @static
29850          */
29851         stopEvent: function(e){
29852             if(this.stopPropagation) {
29853                 e.stopPropagation();
29854             }
29855
29856             if (this.preventDefault) {
29857                 e.preventDefault();
29858             }
29859         },
29860
29861         /**
29862          * Internal function to clean up event handlers after the drag
29863          * operation is complete
29864          * @method stopDrag
29865          * @param {Event} e the event
29866          * @private
29867          * @static
29868          */
29869         stopDrag: function(e) {
29870             // Fire the drag end event for the item that was dragged
29871             if (this.dragCurrent) {
29872                 if (this.dragThreshMet) {
29873                     this.dragCurrent.b4EndDrag(e);
29874                     this.dragCurrent.endDrag(e);
29875                 }
29876
29877                 this.dragCurrent.onMouseUp(e);
29878             }
29879
29880             this.dragCurrent = null;
29881             this.dragOvers = {};
29882         },
29883
29884         /**
29885          * Internal function to handle the mousemove event.  Will be invoked
29886          * from the context of the html element.
29887          *
29888          * @TODO figure out what we can do about mouse events lost when the
29889          * user drags objects beyond the window boundary.  Currently we can
29890          * detect this in internet explorer by verifying that the mouse is
29891          * down during the mousemove event.  Firefox doesn't give us the
29892          * button state on the mousemove event.
29893          * @method handleMouseMove
29894          * @param {Event} e the event
29895          * @private
29896          * @static
29897          */
29898         handleMouseMove: function(e) {
29899             if (! this.dragCurrent) {
29900                 return true;
29901             }
29902             // var button = e.which || e.button;
29903
29904             // check for IE mouseup outside of page boundary
29905             if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
29906                 this.stopEvent(e);
29907                 return this.handleMouseUp(e);
29908             }
29909
29910             if (!this.dragThreshMet) {
29911                 var diffX = Math.abs(this.startX - e.getPageX());
29912                 var diffY = Math.abs(this.startY - e.getPageY());
29913                 if (diffX > this.clickPixelThresh ||
29914                             diffY > this.clickPixelThresh) {
29915                     this.startDrag(this.startX, this.startY);
29916                 }
29917             }
29918
29919             if (this.dragThreshMet) {
29920                 this.dragCurrent.b4Drag(e);
29921                 this.dragCurrent.onDrag(e);
29922                 if(!this.dragCurrent.moveOnly){
29923                     this.fireEvents(e, false);
29924                 }
29925             }
29926
29927             this.stopEvent(e);
29928
29929             return true;
29930         },
29931
29932         /**
29933          * Iterates over all of the DragDrop elements to find ones we are
29934          * hovering over or dropping on
29935          * @method fireEvents
29936          * @param {Event} e the event
29937          * @param {boolean} isDrop is this a drop op or a mouseover op?
29938          * @private
29939          * @static
29940          */
29941         fireEvents: function(e, isDrop) {
29942             var dc = this.dragCurrent;
29943
29944             // If the user did the mouse up outside of the window, we could
29945             // get here even though we have ended the drag.
29946             if (!dc || dc.isLocked()) {
29947                 return;
29948             }
29949
29950             var pt = e.getPoint();
29951
29952             // cache the previous dragOver array
29953             var oldOvers = [];
29954
29955             var outEvts   = [];
29956             var overEvts  = [];
29957             var dropEvts  = [];
29958             var enterEvts = [];
29959
29960             // Check to see if the object(s) we were hovering over is no longer
29961             // being hovered over so we can fire the onDragOut event
29962             for (var i in this.dragOvers) {
29963
29964                 var ddo = this.dragOvers[i];
29965
29966                 if (! this.isTypeOfDD(ddo)) {
29967                     continue;
29968                 }
29969
29970                 if (! this.isOverTarget(pt, ddo, this.mode)) {
29971                     outEvts.push( ddo );
29972                 }
29973
29974                 oldOvers[i] = true;
29975                 delete this.dragOvers[i];
29976             }
29977
29978             for (var sGroup in dc.groups) {
29979
29980                 if ("string" != typeof sGroup) {
29981                     continue;
29982                 }
29983
29984                 for (i in this.ids[sGroup]) {
29985                     var oDD = this.ids[sGroup][i];
29986                     if (! this.isTypeOfDD(oDD)) {
29987                         continue;
29988                     }
29989
29990                     if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
29991                         if (this.isOverTarget(pt, oDD, this.mode)) {
29992                             // look for drop interactions
29993                             if (isDrop) {
29994                                 dropEvts.push( oDD );
29995                             // look for drag enter and drag over interactions
29996                             } else {
29997
29998                                 // initial drag over: dragEnter fires
29999                                 if (!oldOvers[oDD.id]) {
30000                                     enterEvts.push( oDD );
30001                                 // subsequent drag overs: dragOver fires
30002                                 } else {
30003                                     overEvts.push( oDD );
30004                                 }
30005
30006                                 this.dragOvers[oDD.id] = oDD;
30007                             }
30008                         }
30009                     }
30010                 }
30011             }
30012
30013             if (this.mode) {
30014                 if (outEvts.length) {
30015                     dc.b4DragOut(e, outEvts);
30016                     dc.onDragOut(e, outEvts);
30017                 }
30018
30019                 if (enterEvts.length) {
30020                     dc.onDragEnter(e, enterEvts);
30021                 }
30022
30023                 if (overEvts.length) {
30024                     dc.b4DragOver(e, overEvts);
30025                     dc.onDragOver(e, overEvts);
30026                 }
30027
30028                 if (dropEvts.length) {
30029                     dc.b4DragDrop(e, dropEvts);
30030                     dc.onDragDrop(e, dropEvts);
30031                 }
30032
30033             } else {
30034                 // fire dragout events
30035                 var len = 0;
30036                 for (i=0, len=outEvts.length; i<len; ++i) {
30037                     dc.b4DragOut(e, outEvts[i].id);
30038                     dc.onDragOut(e, outEvts[i].id);
30039                 }
30040
30041                 // fire enter events
30042                 for (i=0,len=enterEvts.length; i<len; ++i) {
30043                     // dc.b4DragEnter(e, oDD.id);
30044                     dc.onDragEnter(e, enterEvts[i].id);
30045                 }
30046
30047                 // fire over events
30048                 for (i=0,len=overEvts.length; i<len; ++i) {
30049                     dc.b4DragOver(e, overEvts[i].id);
30050                     dc.onDragOver(e, overEvts[i].id);
30051                 }
30052
30053                 // fire drop events
30054                 for (i=0, len=dropEvts.length; i<len; ++i) {
30055                     dc.b4DragDrop(e, dropEvts[i].id);
30056                     dc.onDragDrop(e, dropEvts[i].id);
30057                 }
30058
30059             }
30060
30061             // notify about a drop that did not find a target
30062             if (isDrop && !dropEvts.length) {
30063                 dc.onInvalidDrop(e);
30064             }
30065
30066         },
30067
30068         /**
30069          * Helper function for getting the best match from the list of drag
30070          * and drop objects returned by the drag and drop events when we are
30071          * in INTERSECT mode.  It returns either the first object that the
30072          * cursor is over, or the object that has the greatest overlap with
30073          * the dragged element.
30074          * @method getBestMatch
30075          * @param  {DragDrop[]} dds The array of drag and drop objects
30076          * targeted
30077          * @return {DragDrop}       The best single match
30078          * @static
30079          */
30080         getBestMatch: function(dds) {
30081             var winner = null;
30082             // Return null if the input is not what we expect
30083             //if (!dds || !dds.length || dds.length == 0) {
30084                // winner = null;
30085             // If there is only one item, it wins
30086             //} else if (dds.length == 1) {
30087
30088             var len = dds.length;
30089
30090             if (len == 1) {
30091                 winner = dds[0];
30092             } else {
30093                 // Loop through the targeted items
30094                 for (var i=0; i<len; ++i) {
30095                     var dd = dds[i];
30096                     // If the cursor is over the object, it wins.  If the
30097                     // cursor is over multiple matches, the first one we come
30098                     // to wins.
30099                     if (dd.cursorIsOver) {
30100                         winner = dd;
30101                         break;
30102                     // Otherwise the object with the most overlap wins
30103                     } else {
30104                         if (!winner ||
30105                             winner.overlap.getArea() < dd.overlap.getArea()) {
30106                             winner = dd;
30107                         }
30108                     }
30109                 }
30110             }
30111
30112             return winner;
30113         },
30114
30115         /**
30116          * Refreshes the cache of the top-left and bottom-right points of the
30117          * drag and drop objects in the specified group(s).  This is in the
30118          * format that is stored in the drag and drop instance, so typical
30119          * usage is:
30120          * <code>
30121          * Ext.dd.DragDropMgr.refreshCache(ddinstance.groups);
30122          * </code>
30123          * Alternatively:
30124          * <code>
30125          * Ext.dd.DragDropMgr.refreshCache({group1:true, group2:true});
30126          * </code>
30127          * @TODO this really should be an indexed array.  Alternatively this
30128          * method could accept both.
30129          * @method refreshCache
30130          * @param {Object} groups an associative array of groups to refresh
30131          * @static
30132          */
30133         refreshCache: function(groups) {
30134             for (var sGroup in groups) {
30135                 if ("string" != typeof sGroup) {
30136                     continue;
30137                 }
30138                 for (var i in this.ids[sGroup]) {
30139                     var oDD = this.ids[sGroup][i];
30140
30141                     if (this.isTypeOfDD(oDD)) {
30142                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
30143                         var loc = this.getLocation(oDD);
30144                         if (loc) {
30145                             this.locationCache[oDD.id] = loc;
30146                         } else {
30147                             delete this.locationCache[oDD.id];
30148                             // this will unregister the drag and drop object if
30149                             // the element is not in a usable state
30150                             // oDD.unreg();
30151                         }
30152                     }
30153                 }
30154             }
30155         },
30156
30157         /**
30158          * This checks to make sure an element exists and is in the DOM.  The
30159          * main purpose is to handle cases where innerHTML is used to remove
30160          * drag and drop objects from the DOM.  IE provides an 'unspecified
30161          * error' when trying to access the offsetParent of such an element
30162          * @method verifyEl
30163          * @param {HTMLElement} el the element to check
30164          * @return {boolean} true if the element looks usable
30165          * @static
30166          */
30167         verifyEl: function(el) {
30168             if (el) {
30169                 var parent;
30170                 if(Ext.isIE){
30171                     try{
30172                         parent = el.offsetParent;
30173                     }catch(e){}
30174                 }else{
30175                     parent = el.offsetParent;
30176                 }
30177                 if (parent) {
30178                     return true;
30179                 }
30180             }
30181
30182             return false;
30183         },
30184
30185         /**
30186          * Returns a Region object containing the drag and drop element's position
30187          * and size, including the padding configured for it
30188          * @method getLocation
30189          * @param {DragDrop} oDD the drag and drop object to get the
30190          *                       location for
30191          * @return {Ext.lib.Region} a Region object representing the total area
30192          *                             the element occupies, including any padding
30193          *                             the instance is configured for.
30194          * @static
30195          */
30196         getLocation: function(oDD) {
30197             if (! this.isTypeOfDD(oDD)) {
30198                 return null;
30199             }
30200
30201             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
30202
30203             try {
30204                 pos= Ext.lib.Dom.getXY(el);
30205             } catch (e) { }
30206
30207             if (!pos) {
30208                 return null;
30209             }
30210
30211             x1 = pos[0];
30212             x2 = x1 + el.offsetWidth;
30213             y1 = pos[1];
30214             y2 = y1 + el.offsetHeight;
30215
30216             t = y1 - oDD.padding[0];
30217             r = x2 + oDD.padding[1];
30218             b = y2 + oDD.padding[2];
30219             l = x1 - oDD.padding[3];
30220
30221             return new Ext.lib.Region( t, r, b, l );
30222         },
30223
30224         /**
30225          * Checks the cursor location to see if it over the target
30226          * @method isOverTarget
30227          * @param {Ext.lib.Point} pt The point to evaluate
30228          * @param {DragDrop} oTarget the DragDrop object we are inspecting
30229          * @return {boolean} true if the mouse is over the target
30230          * @private
30231          * @static
30232          */
30233         isOverTarget: function(pt, oTarget, intersect) {
30234             // use cache if available
30235             var loc = this.locationCache[oTarget.id];
30236             if (!loc || !this.useCache) {
30237                 loc = this.getLocation(oTarget);
30238                 this.locationCache[oTarget.id] = loc;
30239
30240             }
30241
30242             if (!loc) {
30243                 return false;
30244             }
30245
30246             oTarget.cursorIsOver = loc.contains( pt );
30247
30248             // DragDrop is using this as a sanity check for the initial mousedown
30249             // in this case we are done.  In POINT mode, if the drag obj has no
30250             // contraints, we are also done. Otherwise we need to evaluate the
30251             // location of the target as related to the actual location of the
30252             // dragged element.
30253             var dc = this.dragCurrent;
30254             if (!dc || !dc.getTargetCoord ||
30255                     (!intersect && !dc.constrainX && !dc.constrainY)) {
30256                 return oTarget.cursorIsOver;
30257             }
30258
30259             oTarget.overlap = null;
30260
30261             // Get the current location of the drag element, this is the
30262             // location of the mouse event less the delta that represents
30263             // where the original mousedown happened on the element.  We
30264             // need to consider constraints and ticks as well.
30265             var pos = dc.getTargetCoord(pt.x, pt.y);
30266
30267             var el = dc.getDragEl();
30268             var curRegion = new Ext.lib.Region( pos.y,
30269                                                    pos.x + el.offsetWidth,
30270                                                    pos.y + el.offsetHeight,
30271                                                    pos.x );
30272
30273             var overlap = curRegion.intersect(loc);
30274
30275             if (overlap) {
30276                 oTarget.overlap = overlap;
30277                 return (intersect) ? true : oTarget.cursorIsOver;
30278             } else {
30279                 return false;
30280             }
30281         },
30282
30283         /**
30284          * unload event handler
30285          * @method _onUnload
30286          * @private
30287          * @static
30288          */
30289         _onUnload: function(e, me) {
30290             Ext.dd.DragDropMgr.unregAll();
30291         },
30292
30293         /**
30294          * Cleans up the drag and drop events and objects.
30295          * @method unregAll
30296          * @private
30297          * @static
30298          */
30299         unregAll: function() {
30300
30301             if (this.dragCurrent) {
30302                 this.stopDrag();
30303                 this.dragCurrent = null;
30304             }
30305
30306             this._execOnAll("unreg", []);
30307
30308             for (var i in this.elementCache) {
30309                 delete this.elementCache[i];
30310             }
30311
30312             this.elementCache = {};
30313             this.ids = {};
30314         },
30315
30316         /**
30317          * A cache of DOM elements
30318          * @property elementCache
30319          * @private
30320          * @static
30321          */
30322         elementCache: {},
30323
30324         /**
30325          * Get the wrapper for the DOM element specified
30326          * @method getElWrapper
30327          * @param {String} id the id of the element to get
30328          * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
30329          * @private
30330          * @deprecated This wrapper isn't that useful
30331          * @static
30332          */
30333         getElWrapper: function(id) {
30334             var oWrapper = this.elementCache[id];
30335             if (!oWrapper || !oWrapper.el) {
30336                 oWrapper = this.elementCache[id] =
30337                     new this.ElementWrapper(Ext.getDom(id));
30338             }
30339             return oWrapper;
30340         },
30341
30342         /**
30343          * Returns the actual DOM element
30344          * @method getElement
30345          * @param {String} id the id of the elment to get
30346          * @return {Object} The element
30347          * @deprecated use Ext.lib.Ext.getDom instead
30348          * @static
30349          */
30350         getElement: function(id) {
30351             return Ext.getDom(id);
30352         },
30353
30354         /**
30355          * Returns the style property for the DOM element (i.e.,
30356          * document.getElById(id).style)
30357          * @method getCss
30358          * @param {String} id the id of the elment to get
30359          * @return {Object} The style property of the element
30360          * @deprecated use Ext.lib.Dom instead
30361          * @static
30362          */
30363         getCss: function(id) {
30364             var el = Ext.getDom(id);
30365             return (el) ? el.style : null;
30366         },
30367
30368         /**
30369          * Inner class for cached elements
30370          * @class Ext.dd.DragDropMgr.ElementWrapper
30371          * @for DragDropMgr
30372          * @private
30373          * @deprecated
30374          */
30375         ElementWrapper: function(el) {
30376                 /**
30377                  * The element
30378                  * @property el
30379                  */
30380                 this.el = el || null;
30381                 /**
30382                  * The element id
30383                  * @property id
30384                  */
30385                 this.id = this.el && el.id;
30386                 /**
30387                  * A reference to the style property
30388                  * @property css
30389                  */
30390                 this.css = this.el && el.style;
30391             },
30392
30393         /**
30394          * Returns the X position of an html element
30395          * @method getPosX
30396          * @param el the element for which to get the position
30397          * @return {int} the X coordinate
30398          * @for DragDropMgr
30399          * @deprecated use Ext.lib.Dom.getX instead
30400          * @static
30401          */
30402         getPosX: function(el) {
30403             return Ext.lib.Dom.getX(el);
30404         },
30405
30406         /**
30407          * Returns the Y position of an html element
30408          * @method getPosY
30409          * @param el the element for which to get the position
30410          * @return {int} the Y coordinate
30411          * @deprecated use Ext.lib.Dom.getY instead
30412          * @static
30413          */
30414         getPosY: function(el) {
30415             return Ext.lib.Dom.getY(el);
30416         },
30417
30418         /**
30419          * Swap two nodes.  In IE, we use the native method, for others we
30420          * emulate the IE behavior
30421          * @method swapNode
30422          * @param n1 the first node to swap
30423          * @param n2 the other node to swap
30424          * @static
30425          */
30426         swapNode: function(n1, n2) {
30427             if (n1.swapNode) {
30428                 n1.swapNode(n2);
30429             } else {
30430                 var p = n2.parentNode;
30431                 var s = n2.nextSibling;
30432
30433                 if (s == n1) {
30434                     p.insertBefore(n1, n2);
30435                 } else if (n2 == n1.nextSibling) {
30436                     p.insertBefore(n2, n1);
30437                 } else {
30438                     n1.parentNode.replaceChild(n2, n1);
30439                     p.insertBefore(n1, s);
30440                 }
30441             }
30442         },
30443
30444         /**
30445          * Returns the current scroll position
30446          * @method getScroll
30447          * @private
30448          * @static
30449          */
30450         getScroll: function () {
30451             var t, l, dde=document.documentElement, db=document.body;
30452             if (dde && (dde.scrollTop || dde.scrollLeft)) {
30453                 t = dde.scrollTop;
30454                 l = dde.scrollLeft;
30455             } else if (db) {
30456                 t = db.scrollTop;
30457                 l = db.scrollLeft;
30458             } else {
30459
30460             }
30461             return { top: t, left: l };
30462         },
30463
30464         /**
30465          * Returns the specified element style property
30466          * @method getStyle
30467          * @param {HTMLElement} el          the element
30468          * @param {string}      styleProp   the style property
30469          * @return {string} The value of the style property
30470          * @deprecated use Ext.lib.Dom.getStyle
30471          * @static
30472          */
30473         getStyle: function(el, styleProp) {
30474             return Ext.fly(el).getStyle(styleProp);
30475         },
30476
30477         /**
30478          * Gets the scrollTop
30479          * @method getScrollTop
30480          * @return {int} the document's scrollTop
30481          * @static
30482          */
30483         getScrollTop: function () {
30484             return this.getScroll().top;
30485         },
30486
30487         /**
30488          * Gets the scrollLeft
30489          * @method getScrollLeft
30490          * @return {int} the document's scrollTop
30491          * @static
30492          */
30493         getScrollLeft: function () {
30494             return this.getScroll().left;
30495         },
30496
30497         /**
30498          * Sets the x/y position of an element to the location of the
30499          * target element.
30500          * @method moveToEl
30501          * @param {HTMLElement} moveEl      The element to move
30502          * @param {HTMLElement} targetEl    The position reference element
30503          * @static
30504          */
30505         moveToEl: function (moveEl, targetEl) {
30506             var aCoord = Ext.lib.Dom.getXY(targetEl);
30507             Ext.lib.Dom.setXY(moveEl, aCoord);
30508         },
30509
30510         /**
30511          * Numeric array sort function
30512          * @method numericSort
30513          * @static
30514          */
30515         numericSort: function(a, b) {
30516             return (a - b);
30517         },
30518
30519         /**
30520          * Internal counter
30521          * @property _timeoutCount
30522          * @private
30523          * @static
30524          */
30525         _timeoutCount: 0,
30526
30527         /**
30528          * Trying to make the load order less important.  Without this we get
30529          * an error if this file is loaded before the Event Utility.
30530          * @method _addListeners
30531          * @private
30532          * @static
30533          */
30534         _addListeners: function() {
30535             var DDM = Ext.dd.DDM;
30536             if ( Ext.lib.Event && document ) {
30537                 DDM._onLoad();
30538             } else {
30539                 if (DDM._timeoutCount > 2000) {
30540                 } else {
30541                     setTimeout(DDM._addListeners, 10);
30542                     if (document && document.body) {
30543                         DDM._timeoutCount += 1;
30544                     }
30545                 }
30546             }
30547         },
30548
30549         /**
30550          * Recursively searches the immediate parent and all child nodes for
30551          * the handle element in order to determine wheter or not it was
30552          * clicked.
30553          * @method handleWasClicked
30554          * @param node the html element to inspect
30555          * @static
30556          */
30557         handleWasClicked: function(node, id) {
30558             if (this.isHandle(id, node.id)) {
30559                 return true;
30560             } else {
30561                 // check to see if this is a text node child of the one we want
30562                 var p = node.parentNode;
30563
30564                 while (p) {
30565                     if (this.isHandle(id, p.id)) {
30566                         return true;
30567                     } else {
30568                         p = p.parentNode;
30569                     }
30570                 }
30571             }
30572
30573             return false;
30574         }
30575
30576     };
30577
30578 }();
30579
30580 // shorter alias, save a few bytes
30581 Ext.dd.DDM = Ext.dd.DragDropMgr;
30582 Ext.dd.DDM._addListeners();
30583
30584 }
30585
30586 /**
30587  * @class Ext.dd.DD
30588  * A DragDrop implementation where the linked element follows the
30589  * mouse cursor during a drag.
30590  * @extends Ext.dd.DragDrop
30591  * @constructor
30592  * @param {String} id the id of the linked element
30593  * @param {String} sGroup the group of related DragDrop items
30594  * @param {object} config an object containing configurable attributes
30595  *                Valid properties for DD:
30596  *                    scroll
30597  */
30598 Ext.dd.DD = function(id, sGroup, config) {
30599     if (id) {
30600         this.init(id, sGroup, config);
30601     }
30602 };
30603
30604 Ext.extend(Ext.dd.DD, Ext.dd.DragDrop, {
30605
30606     /**
30607      * When set to true, the utility automatically tries to scroll the browser
30608      * window when a drag and drop element is dragged near the viewport boundary.
30609      * Defaults to true.
30610      * @property scroll
30611      * @type boolean
30612      */
30613     scroll: true,
30614
30615     /**
30616      * Sets the pointer offset to the distance between the linked element's top
30617      * left corner and the location the element was clicked
30618      * @method autoOffset
30619      * @param {int} iPageX the X coordinate of the click
30620      * @param {int} iPageY the Y coordinate of the click
30621      */
30622     autoOffset: function(iPageX, iPageY) {
30623         var x = iPageX - this.startPageX;
30624         var y = iPageY - this.startPageY;
30625         this.setDelta(x, y);
30626     },
30627
30628     /**
30629      * Sets the pointer offset.  You can call this directly to force the
30630      * offset to be in a particular location (e.g., pass in 0,0 to set it
30631      * to the center of the object)
30632      * @method setDelta
30633      * @param {int} iDeltaX the distance from the left
30634      * @param {int} iDeltaY the distance from the top
30635      */
30636     setDelta: function(iDeltaX, iDeltaY) {
30637         this.deltaX = iDeltaX;
30638         this.deltaY = iDeltaY;
30639     },
30640
30641     /**
30642      * Sets the drag element to the location of the mousedown or click event,
30643      * maintaining the cursor location relative to the location on the element
30644      * that was clicked.  Override this if you want to place the element in a
30645      * location other than where the cursor is.
30646      * @method setDragElPos
30647      * @param {int} iPageX the X coordinate of the mousedown or drag event
30648      * @param {int} iPageY the Y coordinate of the mousedown or drag event
30649      */
30650     setDragElPos: function(iPageX, iPageY) {
30651         // the first time we do this, we are going to check to make sure
30652         // the element has css positioning
30653
30654         var el = this.getDragEl();
30655         this.alignElWithMouse(el, iPageX, iPageY);
30656     },
30657
30658     /**
30659      * Sets the element to the location of the mousedown or click event,
30660      * maintaining the cursor location relative to the location on the element
30661      * that was clicked.  Override this if you want to place the element in a
30662      * location other than where the cursor is.
30663      * @method alignElWithMouse
30664      * @param {HTMLElement} el the element to move
30665      * @param {int} iPageX the X coordinate of the mousedown or drag event
30666      * @param {int} iPageY the Y coordinate of the mousedown or drag event
30667      */
30668     alignElWithMouse: function(el, iPageX, iPageY) {
30669         var oCoord = this.getTargetCoord(iPageX, iPageY);
30670         var fly = el.dom ? el : Ext.fly(el, '_dd');
30671         if (!this.deltaSetXY) {
30672             var aCoord = [oCoord.x, oCoord.y];
30673             fly.setXY(aCoord);
30674             var newLeft = fly.getLeft(true);
30675             var newTop  = fly.getTop(true);
30676             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
30677         } else {
30678             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
30679         }
30680
30681         this.cachePosition(oCoord.x, oCoord.y);
30682         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
30683         return oCoord;
30684     },
30685
30686     /**
30687      * Saves the most recent position so that we can reset the constraints and
30688      * tick marks on-demand.  We need to know this so that we can calculate the
30689      * number of pixels the element is offset from its original position.
30690      * @method cachePosition
30691      * @param iPageX the current x position (optional, this just makes it so we
30692      * don't have to look it up again)
30693      * @param iPageY the current y position (optional, this just makes it so we
30694      * don't have to look it up again)
30695      */
30696     cachePosition: function(iPageX, iPageY) {
30697         if (iPageX) {
30698             this.lastPageX = iPageX;
30699             this.lastPageY = iPageY;
30700         } else {
30701             var aCoord = Ext.lib.Dom.getXY(this.getEl());
30702             this.lastPageX = aCoord[0];
30703             this.lastPageY = aCoord[1];
30704         }
30705     },
30706
30707     /**
30708      * Auto-scroll the window if the dragged object has been moved beyond the
30709      * visible window boundary.
30710      * @method autoScroll
30711      * @param {int} x the drag element's x position
30712      * @param {int} y the drag element's y position
30713      * @param {int} h the height of the drag element
30714      * @param {int} w the width of the drag element
30715      * @private
30716      */
30717     autoScroll: function(x, y, h, w) {
30718
30719         if (this.scroll) {
30720             // The client height
30721             var clientH = Ext.lib.Dom.getViewHeight();
30722
30723             // The client width
30724             var clientW = Ext.lib.Dom.getViewWidth();
30725
30726             // The amt scrolled down
30727             var st = this.DDM.getScrollTop();
30728
30729             // The amt scrolled right
30730             var sl = this.DDM.getScrollLeft();
30731
30732             // Location of the bottom of the element
30733             var bot = h + y;
30734
30735             // Location of the right of the element
30736             var right = w + x;
30737
30738             // The distance from the cursor to the bottom of the visible area,
30739             // adjusted so that we don't scroll if the cursor is beyond the
30740             // element drag constraints
30741             var toBot = (clientH + st - y - this.deltaY);
30742
30743             // The distance from the cursor to the right of the visible area
30744             var toRight = (clientW + sl - x - this.deltaX);
30745
30746
30747             // How close to the edge the cursor must be before we scroll
30748             // var thresh = (document.all) ? 100 : 40;
30749             var thresh = 40;
30750
30751             // How many pixels to scroll per autoscroll op.  This helps to reduce
30752             // clunky scrolling. IE is more sensitive about this ... it needs this
30753             // value to be higher.
30754             var scrAmt = (document.all) ? 80 : 30;
30755
30756             // Scroll down if we are near the bottom of the visible page and the
30757             // obj extends below the crease
30758             if ( bot > clientH && toBot < thresh ) {
30759                 window.scrollTo(sl, st + scrAmt);
30760             }
30761
30762             // Scroll up if the window is scrolled down and the top of the object
30763             // goes above the top border
30764             if ( y < st && st > 0 && y - st < thresh ) {
30765                 window.scrollTo(sl, st - scrAmt);
30766             }
30767
30768             // Scroll right if the obj is beyond the right border and the cursor is
30769             // near the border.
30770             if ( right > clientW && toRight < thresh ) {
30771                 window.scrollTo(sl + scrAmt, st);
30772             }
30773
30774             // Scroll left if the window has been scrolled to the right and the obj
30775             // extends past the left border
30776             if ( x < sl && sl > 0 && x - sl < thresh ) {
30777                 window.scrollTo(sl - scrAmt, st);
30778             }
30779         }
30780     },
30781
30782     /**
30783      * Finds the location the element should be placed if we want to move
30784      * it to where the mouse location less the click offset would place us.
30785      * @method getTargetCoord
30786      * @param {int} iPageX the X coordinate of the click
30787      * @param {int} iPageY the Y coordinate of the click
30788      * @return an object that contains the coordinates (Object.x and Object.y)
30789      * @private
30790      */
30791     getTargetCoord: function(iPageX, iPageY) {
30792         var x = iPageX - this.deltaX;
30793         var y = iPageY - this.deltaY;
30794
30795         if (this.constrainX) {
30796             if (x < this.minX) { x = this.minX; }
30797             if (x > this.maxX) { x = this.maxX; }
30798         }
30799
30800         if (this.constrainY) {
30801             if (y < this.minY) { y = this.minY; }
30802             if (y > this.maxY) { y = this.maxY; }
30803         }
30804
30805         x = this.getTick(x, this.xTicks);
30806         y = this.getTick(y, this.yTicks);
30807
30808
30809         return {x:x, y:y};
30810     },
30811
30812     /**
30813      * Sets up config options specific to this class. Overrides
30814      * Ext.dd.DragDrop, but all versions of this method through the
30815      * inheritance chain are called
30816      */
30817     applyConfig: function() {
30818         Ext.dd.DD.superclass.applyConfig.call(this);
30819         this.scroll = (this.config.scroll !== false);
30820     },
30821
30822     /**
30823      * Event that fires prior to the onMouseDown event.  Overrides
30824      * Ext.dd.DragDrop.
30825      */
30826     b4MouseDown: function(e) {
30827         // this.resetConstraints();
30828         this.autoOffset(e.getPageX(),
30829                             e.getPageY());
30830     },
30831
30832     /**
30833      * Event that fires prior to the onDrag event.  Overrides
30834      * Ext.dd.DragDrop.
30835      */
30836     b4Drag: function(e) {
30837         this.setDragElPos(e.getPageX(),
30838                             e.getPageY());
30839     },
30840
30841     toString: function() {
30842         return ("DD " + this.id);
30843     }
30844
30845     //////////////////////////////////////////////////////////////////////////
30846     // Debugging ygDragDrop events that can be overridden
30847     //////////////////////////////////////////////////////////////////////////
30848     /*
30849     startDrag: function(x, y) {
30850     },
30851
30852     onDrag: function(e) {
30853     },
30854
30855     onDragEnter: function(e, id) {
30856     },
30857
30858     onDragOver: function(e, id) {
30859     },
30860
30861     onDragOut: function(e, id) {
30862     },
30863
30864     onDragDrop: function(e, id) {
30865     },
30866
30867     endDrag: function(e) {
30868     }
30869
30870     */
30871
30872 });
30873 /**
30874  * @class Ext.dd.DDProxy
30875  * A DragDrop implementation that inserts an empty, bordered div into
30876  * the document that follows the cursor during drag operations.  At the time of
30877  * the click, the frame div is resized to the dimensions of the linked html
30878  * element, and moved to the exact location of the linked element.
30879  *
30880  * References to the "frame" element refer to the single proxy element that
30881  * was created to be dragged in place of all DDProxy elements on the
30882  * page.
30883  *
30884  * @extends Ext.dd.DD
30885  * @constructor
30886  * @param {String} id the id of the linked html element
30887  * @param {String} sGroup the group of related DragDrop objects
30888  * @param {object} config an object containing configurable attributes
30889  *                Valid properties for DDProxy in addition to those in DragDrop:
30890  *                   resizeFrame, centerFrame, dragElId
30891  */
30892 Ext.dd.DDProxy = function(id, sGroup, config) {
30893     if (id) {
30894         this.init(id, sGroup, config);
30895         this.initFrame();
30896     }
30897 };
30898
30899 /**
30900  * The default drag frame div id
30901  * @property Ext.dd.DDProxy.dragElId
30902  * @type String
30903  * @static
30904  */
30905 Ext.dd.DDProxy.dragElId = "ygddfdiv";
30906
30907 Ext.extend(Ext.dd.DDProxy, Ext.dd.DD, {
30908
30909     /**
30910      * By default we resize the drag frame to be the same size as the element
30911      * we want to drag (this is to get the frame effect).  We can turn it off
30912      * if we want a different behavior.
30913      * @property resizeFrame
30914      * @type boolean
30915      */
30916     resizeFrame: true,
30917
30918     /**
30919      * By default the frame is positioned exactly where the drag element is, so
30920      * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
30921      * you do not have constraints on the obj is to have the drag frame centered
30922      * around the cursor.  Set centerFrame to true for this effect.
30923      * @property centerFrame
30924      * @type boolean
30925      */
30926     centerFrame: false,
30927
30928     /**
30929      * Creates the proxy element if it does not yet exist
30930      * @method createFrame
30931      */
30932     createFrame: function() {
30933         var self = this;
30934         var body = document.body;
30935
30936         if (!body || !body.firstChild) {
30937             setTimeout( function() { self.createFrame(); }, 50 );
30938             return;
30939         }
30940
30941         var div = this.getDragEl();
30942
30943         if (!div) {
30944             div    = document.createElement("div");
30945             div.id = this.dragElId;
30946             var s  = div.style;
30947
30948             s.position   = "absolute";
30949             s.visibility = "hidden";
30950             s.cursor     = "move";
30951             s.border     = "2px solid #aaa";
30952             s.zIndex     = 999;
30953
30954             // appendChild can blow up IE if invoked prior to the window load event
30955             // while rendering a table.  It is possible there are other scenarios
30956             // that would cause this to happen as well.
30957             body.insertBefore(div, body.firstChild);
30958         }
30959     },
30960
30961     /**
30962      * Initialization for the drag frame element.  Must be called in the
30963      * constructor of all subclasses
30964      * @method initFrame
30965      */
30966     initFrame: function() {
30967         this.createFrame();
30968     },
30969
30970     applyConfig: function() {
30971         Ext.dd.DDProxy.superclass.applyConfig.call(this);
30972
30973         this.resizeFrame = (this.config.resizeFrame !== false);
30974         this.centerFrame = (this.config.centerFrame);
30975         this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
30976     },
30977
30978     /**
30979      * Resizes the drag frame to the dimensions of the clicked object, positions
30980      * it over the object, and finally displays it
30981      * @method showFrame
30982      * @param {int} iPageX X click position
30983      * @param {int} iPageY Y click position
30984      * @private
30985      */
30986     showFrame: function(iPageX, iPageY) {
30987         var el = this.getEl();
30988         var dragEl = this.getDragEl();
30989         var s = dragEl.style;
30990
30991         this._resizeProxy();
30992
30993         if (this.centerFrame) {
30994             this.setDelta( Math.round(parseInt(s.width,  10)/2),
30995                            Math.round(parseInt(s.height, 10)/2) );
30996         }
30997
30998         this.setDragElPos(iPageX, iPageY);
30999
31000         Ext.fly(dragEl).show();
31001     },
31002
31003     /**
31004      * The proxy is automatically resized to the dimensions of the linked
31005      * element when a drag is initiated, unless resizeFrame is set to false
31006      * @method _resizeProxy
31007      * @private
31008      */
31009     _resizeProxy: function() {
31010         if (this.resizeFrame) {
31011             var el = this.getEl();
31012             Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
31013         }
31014     },
31015
31016     // overrides Ext.dd.DragDrop
31017     b4MouseDown: function(e) {
31018         var x = e.getPageX();
31019         var y = e.getPageY();
31020         this.autoOffset(x, y);
31021         this.setDragElPos(x, y);
31022     },
31023
31024     // overrides Ext.dd.DragDrop
31025     b4StartDrag: function(x, y) {
31026         // show the drag frame
31027         this.showFrame(x, y);
31028     },
31029
31030     // overrides Ext.dd.DragDrop
31031     b4EndDrag: function(e) {
31032         Ext.fly(this.getDragEl()).hide();
31033     },
31034
31035     // overrides Ext.dd.DragDrop
31036     // By default we try to move the element to the last location of the frame.
31037     // This is so that the default behavior mirrors that of Ext.dd.DD.
31038     endDrag: function(e) {
31039
31040         var lel = this.getEl();
31041         var del = this.getDragEl();
31042
31043         // Show the drag frame briefly so we can get its position
31044         del.style.visibility = "";
31045
31046         this.beforeMove();
31047         // Hide the linked element before the move to get around a Safari
31048         // rendering bug.
31049         lel.style.visibility = "hidden";
31050         Ext.dd.DDM.moveToEl(lel, del);
31051         del.style.visibility = "hidden";
31052         lel.style.visibility = "";
31053
31054         this.afterDrag();
31055     },
31056
31057     beforeMove : function(){
31058
31059     },
31060
31061     afterDrag : function(){
31062
31063     },
31064
31065     toString: function() {
31066         return ("DDProxy " + this.id);
31067     }
31068
31069 });
31070 /**
31071  * @class Ext.dd.DDTarget
31072  * A DragDrop implementation that does not move, but can be a drop
31073  * target.  You would get the same result by simply omitting implementation
31074  * for the event callbacks, but this way we reduce the processing cost of the
31075  * event listener and the callbacks.
31076  * @extends Ext.dd.DragDrop
31077  * @constructor
31078  * @param {String} id the id of the element that is a drop target
31079  * @param {String} sGroup the group of related DragDrop objects
31080  * @param {object} config an object containing configurable attributes
31081  *                 Valid properties for DDTarget in addition to those in
31082  *                 DragDrop:
31083  *                    none
31084  */
31085 Ext.dd.DDTarget = function(id, sGroup, config) {
31086     if (id) {
31087         this.initTarget(id, sGroup, config);
31088     }
31089 };
31090
31091 // Ext.dd.DDTarget.prototype = new Ext.dd.DragDrop();
31092 Ext.extend(Ext.dd.DDTarget, Ext.dd.DragDrop, {
31093     /**
31094      * @hide
31095      * Overridden and disabled. A DDTarget does not support being dragged.
31096      * @method
31097      */
31098     getDragEl: Ext.emptyFn,
31099     /**
31100      * @hide
31101      * Overridden and disabled. A DDTarget does not support being dragged.
31102      * @method
31103      */
31104     isValidHandleChild: Ext.emptyFn,
31105     /**
31106      * @hide
31107      * Overridden and disabled. A DDTarget does not support being dragged.
31108      * @method
31109      */
31110     startDrag: Ext.emptyFn,
31111     /**
31112      * @hide
31113      * Overridden and disabled. A DDTarget does not support being dragged.
31114      * @method
31115      */
31116     endDrag: Ext.emptyFn,
31117     /**
31118      * @hide
31119      * Overridden and disabled. A DDTarget does not support being dragged.
31120      * @method
31121      */
31122     onDrag: Ext.emptyFn,
31123     /**
31124      * @hide
31125      * Overridden and disabled. A DDTarget does not support being dragged.
31126      * @method
31127      */
31128     onDragDrop: Ext.emptyFn,
31129     /**
31130      * @hide
31131      * Overridden and disabled. A DDTarget does not support being dragged.
31132      * @method
31133      */
31134     onDragEnter: Ext.emptyFn,
31135     /**
31136      * @hide
31137      * Overridden and disabled. A DDTarget does not support being dragged.
31138      * @method
31139      */
31140     onDragOut: Ext.emptyFn,
31141     /**
31142      * @hide
31143      * Overridden and disabled. A DDTarget does not support being dragged.
31144      * @method
31145      */
31146     onDragOver: Ext.emptyFn,
31147     /**
31148      * @hide
31149      * Overridden and disabled. A DDTarget does not support being dragged.
31150      * @method
31151      */
31152     onInvalidDrop: Ext.emptyFn,
31153     /**
31154      * @hide
31155      * Overridden and disabled. A DDTarget does not support being dragged.
31156      * @method
31157      */
31158     onMouseDown: Ext.emptyFn,
31159     /**
31160      * @hide
31161      * Overridden and disabled. A DDTarget does not support being dragged.
31162      * @method
31163      */
31164     onMouseUp: Ext.emptyFn,
31165     /**
31166      * @hide
31167      * Overridden and disabled. A DDTarget does not support being dragged.
31168      * @method
31169      */
31170     setXConstraint: Ext.emptyFn,
31171     /**
31172      * @hide
31173      * Overridden and disabled. A DDTarget does not support being dragged.
31174      * @method
31175      */
31176     setYConstraint: Ext.emptyFn,
31177     /**
31178      * @hide
31179      * Overridden and disabled. A DDTarget does not support being dragged.
31180      * @method
31181      */
31182     resetConstraints: Ext.emptyFn,
31183     /**
31184      * @hide
31185      * Overridden and disabled. A DDTarget does not support being dragged.
31186      * @method
31187      */
31188     clearConstraints: Ext.emptyFn,
31189     /**
31190      * @hide
31191      * Overridden and disabled. A DDTarget does not support being dragged.
31192      * @method
31193      */
31194     clearTicks: Ext.emptyFn,
31195     /**
31196      * @hide
31197      * Overridden and disabled. A DDTarget does not support being dragged.
31198      * @method
31199      */
31200     setInitPosition: Ext.emptyFn,
31201     /**
31202      * @hide
31203      * Overridden and disabled. A DDTarget does not support being dragged.
31204      * @method
31205      */
31206     setDragElId: Ext.emptyFn,
31207     /**
31208      * @hide
31209      * Overridden and disabled. A DDTarget does not support being dragged.
31210      * @method
31211      */
31212     setHandleElId: Ext.emptyFn,
31213     /**
31214      * @hide
31215      * Overridden and disabled. A DDTarget does not support being dragged.
31216      * @method
31217      */
31218     setOuterHandleElId: Ext.emptyFn,
31219     /**
31220      * @hide
31221      * Overridden and disabled. A DDTarget does not support being dragged.
31222      * @method
31223      */
31224     addInvalidHandleClass: Ext.emptyFn,
31225     /**
31226      * @hide
31227      * Overridden and disabled. A DDTarget does not support being dragged.
31228      * @method
31229      */
31230     addInvalidHandleId: Ext.emptyFn,
31231     /**
31232      * @hide
31233      * Overridden and disabled. A DDTarget does not support being dragged.
31234      * @method
31235      */
31236     addInvalidHandleType: Ext.emptyFn,
31237     /**
31238      * @hide
31239      * Overridden and disabled. A DDTarget does not support being dragged.
31240      * @method
31241      */
31242     removeInvalidHandleClass: Ext.emptyFn,
31243     /**
31244      * @hide
31245      * Overridden and disabled. A DDTarget does not support being dragged.
31246      * @method
31247      */
31248     removeInvalidHandleId: Ext.emptyFn,
31249     /**
31250      * @hide
31251      * Overridden and disabled. A DDTarget does not support being dragged.
31252      * @method
31253      */
31254     removeInvalidHandleType: Ext.emptyFn,
31255
31256     toString: function() {
31257         return ("DDTarget " + this.id);
31258     }
31259 });/**
31260  * @class Ext.dd.DragTracker
31261  * @extends Ext.util.Observable
31262  * A DragTracker listens for drag events on an Element and fires events at the start and end of the drag,
31263  * as well as during the drag. This is useful for components such as {@link Ext.Slider}, where there is
31264  * an element that can be dragged around to change the Slider's value.
31265  * DragTracker provides a series of template methods that should be overridden to provide functionality
31266  * in response to detected drag operations. These are onBeforeStart, onStart, onDrag and onEnd.
31267  * See {@link Ext.Slider}'s initEvents function for an example implementation.
31268  */
31269 Ext.dd.DragTracker = Ext.extend(Ext.util.Observable,  {    
31270     /**
31271      * @cfg {Boolean} active
31272          * Defaults to <tt>false</tt>.
31273          */     
31274     active: false,
31275     /**
31276      * @cfg {Number} tolerance
31277          * Number of pixels the drag target must be moved before dragging is considered to have started. Defaults to <tt>5</tt>.
31278          */     
31279     tolerance: 5,
31280     /**
31281      * @cfg {Boolean/Number} autoStart
31282          * Defaults to <tt>false</tt>. Specify <tt>true</tt> to defer trigger start by 1000 ms.
31283          * Specify a Number for the number of milliseconds to defer trigger start.
31284          */     
31285     autoStart: false,
31286     
31287     constructor : function(config){
31288         Ext.apply(this, config);
31289             this.addEvents(
31290                 /**
31291                  * @event mousedown
31292                  * @param {Object} this
31293                  * @param {Object} e event object
31294                  */
31295                 'mousedown',
31296                 /**
31297                  * @event mouseup
31298                  * @param {Object} this
31299                  * @param {Object} e event object
31300                  */
31301                 'mouseup',
31302                 /**
31303                  * @event mousemove
31304                  * @param {Object} this
31305                  * @param {Object} e event object
31306                  */
31307                 'mousemove',
31308                 /**
31309                  * @event dragstart
31310                  * @param {Object} this
31311                  * @param {Object} startXY the page coordinates of the event
31312                  */
31313                 'dragstart',
31314                 /**
31315                  * @event dragend
31316                  * @param {Object} this
31317                  * @param {Object} e event object
31318                  */
31319                 'dragend',
31320                 /**
31321                  * @event drag
31322                  * @param {Object} this
31323                  * @param {Object} e event object
31324                  */
31325                 'drag'
31326             );
31327         
31328             this.dragRegion = new Ext.lib.Region(0,0,0,0);
31329         
31330             if(this.el){
31331                 this.initEl(this.el);
31332             }
31333         Ext.dd.DragTracker.superclass.constructor.call(this, config);
31334     },
31335
31336     initEl: function(el){
31337         this.el = Ext.get(el);
31338         el.on('mousedown', this.onMouseDown, this,
31339                 this.delegate ? {delegate: this.delegate} : undefined);
31340     },
31341
31342     destroy : function(){
31343         this.el.un('mousedown', this.onMouseDown, this);
31344     },
31345
31346     onMouseDown: function(e, target){
31347         if(this.fireEvent('mousedown', this, e) !== false && this.onBeforeStart(e) !== false){
31348             this.startXY = this.lastXY = e.getXY();
31349             this.dragTarget = this.delegate ? target : this.el.dom;
31350             if(this.preventDefault !== false){
31351                 e.preventDefault();
31352             }
31353             var doc = Ext.getDoc();
31354             doc.on('mouseup', this.onMouseUp, this);
31355             doc.on('mousemove', this.onMouseMove, this);
31356             doc.on('selectstart', this.stopSelect, this);
31357             if(this.autoStart){
31358                 this.timer = this.triggerStart.defer(this.autoStart === true ? 1000 : this.autoStart, this);
31359             }
31360         }
31361     },
31362
31363     onMouseMove: function(e, target){
31364         // HACK: IE hack to see if button was released outside of window. */
31365         if(this.active && Ext.isIE && !e.browserEvent.button){
31366             e.preventDefault();
31367             this.onMouseUp(e);
31368             return;
31369         }
31370
31371         e.preventDefault();
31372         var xy = e.getXY(), s = this.startXY;
31373         this.lastXY = xy;
31374         if(!this.active){
31375             if(Math.abs(s[0]-xy[0]) > this.tolerance || Math.abs(s[1]-xy[1]) > this.tolerance){
31376                 this.triggerStart();
31377             }else{
31378                 return;
31379             }
31380         }
31381         this.fireEvent('mousemove', this, e);
31382         this.onDrag(e);
31383         this.fireEvent('drag', this, e);
31384     },
31385
31386     onMouseUp: function(e) {
31387         var doc = Ext.getDoc();
31388         doc.un('mousemove', this.onMouseMove, this);
31389         doc.un('mouseup', this.onMouseUp, this);
31390         doc.un('selectstart', this.stopSelect, this);
31391         e.preventDefault();
31392         this.clearStart();
31393         var wasActive = this.active;
31394         this.active = false;
31395         delete this.elRegion;
31396         this.fireEvent('mouseup', this, e);
31397         if(wasActive){
31398             this.onEnd(e);
31399             this.fireEvent('dragend', this, e);
31400         }
31401     },
31402
31403     triggerStart: function(isTimer) {
31404         this.clearStart();
31405         this.active = true;
31406         this.onStart(this.startXY);
31407         this.fireEvent('dragstart', this, this.startXY);
31408     },
31409
31410     clearStart : function() {
31411         if(this.timer){
31412             clearTimeout(this.timer);
31413             delete this.timer;
31414         }
31415     },
31416
31417     stopSelect : function(e) {
31418         e.stopEvent();
31419         return false;
31420     },
31421     
31422     /**
31423      * Template method which should be overridden by each DragTracker instance. Called when the user first clicks and
31424      * holds the mouse button down. Return false to disallow the drag
31425      * @param {Ext.EventObject} e The event object
31426      */
31427     onBeforeStart : function(e) {
31428
31429     },
31430
31431     /**
31432      * Template method which should be overridden by each DragTracker instance. Called when a drag operation starts
31433      * (e.g. the user has moved the tracked element beyond the specified tolerance)
31434      * @param {Array} xy x and y co-ordinates of the original location of the tracked element
31435      */
31436     onStart : function(xy) {
31437
31438     },
31439
31440     /**
31441      * Template method which should be overridden by each DragTracker instance. Called whenever a drag has been detected.
31442      * @param {Ext.EventObject} e The event object
31443      */
31444     onDrag : function(e) {
31445
31446     },
31447
31448     /**
31449      * Template method which should be overridden by each DragTracker instance. Called when a drag operation has been completed
31450      * (e.g. the user clicked and held the mouse down, dragged the element and then released the mouse button)
31451      * @param {Ext.EventObject} e The event object
31452      */
31453     onEnd : function(e) {
31454
31455     },
31456
31457     /**
31458      * Returns the drag target
31459      * @return {Ext.Element} The element currently being tracked
31460      */
31461     getDragTarget : function(){
31462         return this.dragTarget;
31463     },
31464
31465     getDragCt : function(){
31466         return this.el;
31467     },
31468
31469     getXY : function(constrain){
31470         return constrain ?
31471                this.constrainModes[constrain].call(this, this.lastXY) : this.lastXY;
31472     },
31473
31474     getOffset : function(constrain){
31475         var xy = this.getXY(constrain);
31476         var s = this.startXY;
31477         return [s[0]-xy[0], s[1]-xy[1]];
31478     },
31479
31480     constrainModes: {
31481         'point' : function(xy){
31482
31483             if(!this.elRegion){
31484                 this.elRegion = this.getDragCt().getRegion();
31485             }
31486
31487             var dr = this.dragRegion;
31488
31489             dr.left = xy[0];
31490             dr.top = xy[1];
31491             dr.right = xy[0];
31492             dr.bottom = xy[1];
31493
31494             dr.constrainTo(this.elRegion);
31495
31496             return [dr.left, dr.top];
31497         }
31498     }
31499 });/**
31500  * @class Ext.dd.ScrollManager
31501  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>
31502  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,
31503  * but you can also override most of the configs per scroll container by adding a 
31504  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},
31505  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:
31506  * <pre><code>
31507 var el = Ext.get('scroll-ct');
31508 el.ddScrollConfig = {
31509     vthresh: 50,
31510     hthresh: -1,
31511     frequency: 100,
31512     increment: 200
31513 };
31514 Ext.dd.ScrollManager.register(el);
31515 </code></pre>
31516  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
31517  * @singleton
31518  */
31519 Ext.dd.ScrollManager = function(){
31520     var ddm = Ext.dd.DragDropMgr;
31521     var els = {};
31522     var dragEl = null;
31523     var proc = {};
31524     
31525     var onStop = function(e){
31526         dragEl = null;
31527         clearProc();
31528     };
31529     
31530     var triggerRefresh = function(){
31531         if(ddm.dragCurrent){
31532              ddm.refreshCache(ddm.dragCurrent.groups);
31533         }
31534     };
31535     
31536     var doScroll = function(){
31537         if(ddm.dragCurrent){
31538             var dds = Ext.dd.ScrollManager;
31539             var inc = proc.el.ddScrollConfig ?
31540                       proc.el.ddScrollConfig.increment : dds.increment;
31541             if(!dds.animate){
31542                 if(proc.el.scroll(proc.dir, inc)){
31543                     triggerRefresh();
31544                 }
31545             }else{
31546                 proc.el.scroll(proc.dir, inc, true, dds.animDuration, triggerRefresh);
31547             }
31548         }
31549     };
31550     
31551     var clearProc = function(){
31552         if(proc.id){
31553             clearInterval(proc.id);
31554         }
31555         proc.id = 0;
31556         proc.el = null;
31557         proc.dir = "";
31558     };
31559     
31560     var startProc = function(el, dir){
31561         clearProc();
31562         proc.el = el;
31563         proc.dir = dir;
31564         var freq = (el.ddScrollConfig && el.ddScrollConfig.frequency) ? 
31565                 el.ddScrollConfig.frequency : Ext.dd.ScrollManager.frequency;
31566         proc.id = setInterval(doScroll, freq);
31567     };
31568     
31569     var onFire = function(e, isDrop){
31570         if(isDrop || !ddm.dragCurrent){ return; }
31571         var dds = Ext.dd.ScrollManager;
31572         if(!dragEl || dragEl != ddm.dragCurrent){
31573             dragEl = ddm.dragCurrent;
31574             // refresh regions on drag start
31575             dds.refreshCache();
31576         }
31577         
31578         var xy = Ext.lib.Event.getXY(e);
31579         var pt = new Ext.lib.Point(xy[0], xy[1]);
31580         for(var id in els){
31581             var el = els[id], r = el._region;
31582             var c = el.ddScrollConfig ? el.ddScrollConfig : dds;
31583             if(r && r.contains(pt) && el.isScrollable()){
31584                 if(r.bottom - pt.y <= c.vthresh){
31585                     if(proc.el != el){
31586                         startProc(el, "down");
31587                     }
31588                     return;
31589                 }else if(r.right - pt.x <= c.hthresh){
31590                     if(proc.el != el){
31591                         startProc(el, "left");
31592                     }
31593                     return;
31594                 }else if(pt.y - r.top <= c.vthresh){
31595                     if(proc.el != el){
31596                         startProc(el, "up");
31597                     }
31598                     return;
31599                 }else if(pt.x - r.left <= c.hthresh){
31600                     if(proc.el != el){
31601                         startProc(el, "right");
31602                     }
31603                     return;
31604                 }
31605             }
31606         }
31607         clearProc();
31608     };
31609     
31610     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
31611     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
31612     
31613     return {
31614         /**
31615          * Registers new overflow element(s) to auto scroll
31616          * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either
31617          */
31618         register : function(el){
31619             if(Ext.isArray(el)){
31620                 for(var i = 0, len = el.length; i < len; i++) {
31621                         this.register(el[i]);
31622                 }
31623             }else{
31624                 el = Ext.get(el);
31625                 els[el.id] = el;
31626             }
31627         },
31628         
31629         /**
31630          * Unregisters overflow element(s) so they are no longer scrolled
31631          * @param {Mixed/Array} el The id of or the element to be removed or an array of either
31632          */
31633         unregister : function(el){
31634             if(Ext.isArray(el)){
31635                 for(var i = 0, len = el.length; i < len; i++) {
31636                         this.unregister(el[i]);
31637                 }
31638             }else{
31639                 el = Ext.get(el);
31640                 delete els[el.id];
31641             }
31642         },
31643         
31644         /**
31645          * The number of pixels from the top or bottom edge of a container the pointer needs to be to
31646          * trigger scrolling (defaults to 25)
31647          * @type Number
31648          */
31649         vthresh : 25,
31650         /**
31651          * The number of pixels from the right or left edge of a container the pointer needs to be to
31652          * trigger scrolling (defaults to 25)
31653          * @type Number
31654          */
31655         hthresh : 25,
31656
31657         /**
31658          * The number of pixels to scroll in each scroll increment (defaults to 50)
31659          * @type Number
31660          */
31661         increment : 100,
31662         
31663         /**
31664          * The frequency of scrolls in milliseconds (defaults to 500)
31665          * @type Number
31666          */
31667         frequency : 500,
31668         
31669         /**
31670          * True to animate the scroll (defaults to true)
31671          * @type Boolean
31672          */
31673         animate: true,
31674         
31675         /**
31676          * The animation duration in seconds - 
31677          * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)
31678          * @type Number
31679          */
31680         animDuration: .4,
31681         
31682         /**
31683          * Manually trigger a cache refresh.
31684          */
31685         refreshCache : function(){
31686             for(var id in els){
31687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
31688                     els[id]._region = els[id].getRegion();
31689                 }
31690             }
31691         }
31692     };
31693 }();/**
31694  * @class Ext.dd.Registry
31695  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
31696  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
31697  * @singleton
31698  */
31699 Ext.dd.Registry = function(){
31700     var elements = {}; 
31701     var handles = {}; 
31702     var autoIdSeed = 0;
31703
31704     var getId = function(el, autogen){
31705         if(typeof el == "string"){
31706             return el;
31707         }
31708         var id = el.id;
31709         if(!id && autogen !== false){
31710             id = "extdd-" + (++autoIdSeed);
31711             el.id = id;
31712         }
31713         return id;
31714     };
31715     
31716     return {
31717     /**
31718      * Resgister a drag drop element
31719      * @param {String/HTMLElement} element The id or DOM node to register
31720      * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved
31721      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
31722      * knows how to interpret, plus there are some specific properties known to the Registry that should be
31723      * populated in the data object (if applicable):
31724      * <pre>
31725 Value      Description<br />
31726 ---------  ------------------------------------------<br />
31727 handles    Array of DOM nodes that trigger dragging<br />
31728            for the element being registered<br />
31729 isHandle   True if the element passed in triggers<br />
31730            dragging itself, else false
31731 </pre>
31732      */
31733         register : function(el, data){
31734             data = data || {};
31735             if(typeof el == "string"){
31736                 el = document.getElementById(el);
31737             }
31738             data.ddel = el;
31739             elements[getId(el)] = data;
31740             if(data.isHandle !== false){
31741                 handles[data.ddel.id] = data;
31742             }
31743             if(data.handles){
31744                 var hs = data.handles;
31745                 for(var i = 0, len = hs.length; i < len; i++){
31746                         handles[getId(hs[i])] = data;
31747                 }
31748             }
31749         },
31750
31751     /**
31752      * Unregister a drag drop element
31753      * @param {String/HTMLElement} element The id or DOM node to unregister
31754      */
31755         unregister : function(el){
31756             var id = getId(el, false);
31757             var data = elements[id];
31758             if(data){
31759                 delete elements[id];
31760                 if(data.handles){
31761                     var hs = data.handles;
31762                     for(var i = 0, len = hs.length; i < len; i++){
31763                         delete handles[getId(hs[i], false)];
31764                     }
31765                 }
31766             }
31767         },
31768
31769     /**
31770      * Returns the handle registered for a DOM Node by id
31771      * @param {String/HTMLElement} id The DOM node or id to look up
31772      * @return {Object} handle The custom handle data
31773      */
31774         getHandle : function(id){
31775             if(typeof id != "string"){ // must be element?
31776                 id = id.id;
31777             }
31778             return handles[id];
31779         },
31780
31781     /**
31782      * Returns the handle that is registered for the DOM node that is the target of the event
31783      * @param {Event} e The event
31784      * @return {Object} handle The custom handle data
31785      */
31786         getHandleFromEvent : function(e){
31787             var t = Ext.lib.Event.getTarget(e);
31788             return t ? handles[t.id] : null;
31789         },
31790
31791     /**
31792      * Returns a custom data object that is registered for a DOM node by id
31793      * @param {String/HTMLElement} id The DOM node or id to look up
31794      * @return {Object} data The custom data
31795      */
31796         getTarget : function(id){
31797             if(typeof id != "string"){ // must be element?
31798                 id = id.id;
31799             }
31800             return elements[id];
31801         },
31802
31803     /**
31804      * Returns a custom data object that is registered for the DOM node that is the target of the event
31805      * @param {Event} e The event
31806      * @return {Object} data The custom data
31807      */
31808         getTargetFromEvent : function(e){
31809             var t = Ext.lib.Event.getTarget(e);
31810             return t ? elements[t.id] || handles[t.id] : null;
31811         }
31812     };
31813 }();/**
31814  * @class Ext.dd.StatusProxy
31815  * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the
31816  * default drag proxy used by all Ext.dd components.
31817  * @constructor
31818  * @param {Object} config
31819  */
31820 Ext.dd.StatusProxy = function(config){
31821     Ext.apply(this, config);
31822     this.id = this.id || Ext.id();
31823     this.el = new Ext.Layer({
31824         dh: {
31825             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
31826                 {tag: "div", cls: "x-dd-drop-icon"},
31827                 {tag: "div", cls: "x-dd-drag-ghost"}
31828             ]
31829         }, 
31830         shadow: !config || config.shadow !== false
31831     });
31832     this.ghost = Ext.get(this.el.dom.childNodes[1]);
31833     this.dropStatus = this.dropNotAllowed;
31834 };
31835
31836 Ext.dd.StatusProxy.prototype = {
31837     /**
31838      * @cfg {String} dropAllowed
31839      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
31840      */
31841     dropAllowed : "x-dd-drop-ok",
31842     /**
31843      * @cfg {String} dropNotAllowed
31844      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
31845      */
31846     dropNotAllowed : "x-dd-drop-nodrop",
31847
31848     /**
31849      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
31850      * over the current target element.
31851      * @param {String} cssClass The css class for the new drop status indicator image
31852      */
31853     setStatus : function(cssClass){
31854         cssClass = cssClass || this.dropNotAllowed;
31855         if(this.dropStatus != cssClass){
31856             this.el.replaceClass(this.dropStatus, cssClass);
31857             this.dropStatus = cssClass;
31858         }
31859     },
31860
31861     /**
31862      * Resets the status indicator to the default dropNotAllowed value
31863      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
31864      */
31865     reset : function(clearGhost){
31866         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
31867         this.dropStatus = this.dropNotAllowed;
31868         if(clearGhost){
31869             this.ghost.update("");
31870         }
31871     },
31872
31873     /**
31874      * Updates the contents of the ghost element
31875      * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a
31876      * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).
31877      */
31878     update : function(html){
31879         if(typeof html == "string"){
31880             this.ghost.update(html);
31881         }else{
31882             this.ghost.update("");
31883             html.style.margin = "0";
31884             this.ghost.dom.appendChild(html);
31885         }
31886         var el = this.ghost.dom.firstChild; 
31887         if(el){
31888             Ext.fly(el).setStyle('float', 'none');
31889         }
31890     },
31891
31892     /**
31893      * Returns the underlying proxy {@link Ext.Layer}
31894      * @return {Ext.Layer} el
31895     */
31896     getEl : function(){
31897         return this.el;
31898     },
31899
31900     /**
31901      * Returns the ghost element
31902      * @return {Ext.Element} el
31903      */
31904     getGhost : function(){
31905         return this.ghost;
31906     },
31907
31908     /**
31909      * Hides the proxy
31910      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
31911      */
31912     hide : function(clear){
31913         this.el.hide();
31914         if(clear){
31915             this.reset(true);
31916         }
31917     },
31918
31919     /**
31920      * Stops the repair animation if it's currently running
31921      */
31922     stop : function(){
31923         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
31924             this.anim.stop();
31925         }
31926     },
31927
31928     /**
31929      * Displays this proxy
31930      */
31931     show : function(){
31932         this.el.show();
31933     },
31934
31935     /**
31936      * Force the Layer to sync its shadow and shim positions to the element
31937      */
31938     sync : function(){
31939         this.el.sync();
31940     },
31941
31942     /**
31943      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
31944      * invalid drop operation by the item being dragged.
31945      * @param {Array} xy The XY position of the element ([x, y])
31946      * @param {Function} callback The function to call after the repair is complete.
31947      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
31948      */
31949     repair : function(xy, callback, scope){
31950         this.callback = callback;
31951         this.scope = scope;
31952         if(xy && this.animRepair !== false){
31953             this.el.addClass("x-dd-drag-repair");
31954             this.el.hideUnders(true);
31955             this.anim = this.el.shift({
31956                 duration: this.repairDuration || .5,
31957                 easing: 'easeOut',
31958                 xy: xy,
31959                 stopFx: true,
31960                 callback: this.afterRepair,
31961                 scope: this
31962             });
31963         }else{
31964             this.afterRepair();
31965         }
31966     },
31967
31968     // private
31969     afterRepair : function(){
31970         this.hide(true);
31971         if(typeof this.callback == "function"){
31972             this.callback.call(this.scope || this);
31973         }
31974         this.callback = null;
31975         this.scope = null;
31976     },
31977     
31978     destroy: function(){
31979         Ext.destroy(this.ghost, this.el);    
31980     }
31981 };/**
31982  * @class Ext.dd.DragSource
31983  * @extends Ext.dd.DDProxy
31984  * A simple class that provides the basic implementation needed to make any element draggable.
31985  * @constructor
31986  * @param {Mixed} el The container element
31987  * @param {Object} config
31988  */
31989 Ext.dd.DragSource = function(el, config){
31990     this.el = Ext.get(el);
31991     if(!this.dragData){
31992         this.dragData = {};
31993     }
31994     
31995     Ext.apply(this, config);
31996     
31997     if(!this.proxy){
31998         this.proxy = new Ext.dd.StatusProxy();
31999     }
32000     Ext.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
32001           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
32002     
32003     this.dragging = false;
32004 };
32005
32006 Ext.extend(Ext.dd.DragSource, Ext.dd.DDProxy, {
32007     /**
32008      * @cfg {String} ddGroup
32009      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
32010      * interact with other drag drop objects in the same group (defaults to undefined).
32011      */
32012     /**
32013      * @cfg {String} dropAllowed
32014      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
32015      */
32016     dropAllowed : "x-dd-drop-ok",
32017     /**
32018      * @cfg {String} dropNotAllowed
32019      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
32020      */
32021     dropNotAllowed : "x-dd-drop-nodrop",
32022
32023     /**
32024      * Returns the data object associated with this drag source
32025      * @return {Object} data An object containing arbitrary data
32026      */
32027     getDragData : function(e){
32028         return this.dragData;
32029     },
32030
32031     // private
32032     onDragEnter : function(e, id){
32033         var target = Ext.dd.DragDropMgr.getDDById(id);
32034         this.cachedTarget = target;
32035         if(this.beforeDragEnter(target, e, id) !== false){
32036             if(target.isNotifyTarget){
32037                 var status = target.notifyEnter(this, e, this.dragData);
32038                 this.proxy.setStatus(status);
32039             }else{
32040                 this.proxy.setStatus(this.dropAllowed);
32041             }
32042             
32043             if(this.afterDragEnter){
32044                 /**
32045                  * An empty function by default, but provided so that you can perform a custom action
32046                  * when the dragged item enters the drop target by providing an implementation.
32047                  * @param {Ext.dd.DragDrop} target The drop target
32048                  * @param {Event} e The event object
32049                  * @param {String} id The id of the dragged element
32050                  * @method afterDragEnter
32051                  */
32052                 this.afterDragEnter(target, e, id);
32053             }
32054         }
32055     },
32056
32057     /**
32058      * An empty function by default, but provided so that you can perform a custom action
32059      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
32060      * @param {Ext.dd.DragDrop} target The drop target
32061      * @param {Event} e The event object
32062      * @param {String} id The id of the dragged element
32063      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32064      */
32065     beforeDragEnter : function(target, e, id){
32066         return true;
32067     },
32068
32069     // private
32070     alignElWithMouse: function() {
32071         Ext.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
32072         this.proxy.sync();
32073     },
32074
32075     // private
32076     onDragOver : function(e, id){
32077         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
32078         if(this.beforeDragOver(target, e, id) !== false){
32079             if(target.isNotifyTarget){
32080                 var status = target.notifyOver(this, e, this.dragData);
32081                 this.proxy.setStatus(status);
32082             }
32083
32084             if(this.afterDragOver){
32085                 /**
32086                  * An empty function by default, but provided so that you can perform a custom action
32087                  * while the dragged item is over the drop target by providing an implementation.
32088                  * @param {Ext.dd.DragDrop} target The drop target
32089                  * @param {Event} e The event object
32090                  * @param {String} id The id of the dragged element
32091                  * @method afterDragOver
32092                  */
32093                 this.afterDragOver(target, e, id);
32094             }
32095         }
32096     },
32097
32098     /**
32099      * An empty function by default, but provided so that you can perform a custom action
32100      * while the dragged item is over the drop target and optionally cancel the onDragOver.
32101      * @param {Ext.dd.DragDrop} target The drop target
32102      * @param {Event} e The event object
32103      * @param {String} id The id of the dragged element
32104      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32105      */
32106     beforeDragOver : function(target, e, id){
32107         return true;
32108     },
32109
32110     // private
32111     onDragOut : function(e, id){
32112         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
32113         if(this.beforeDragOut(target, e, id) !== false){
32114             if(target.isNotifyTarget){
32115                 target.notifyOut(this, e, this.dragData);
32116             }
32117             this.proxy.reset();
32118             if(this.afterDragOut){
32119                 /**
32120                  * An empty function by default, but provided so that you can perform a custom action
32121                  * after the dragged item is dragged out of the target without dropping.
32122                  * @param {Ext.dd.DragDrop} target The drop target
32123                  * @param {Event} e The event object
32124                  * @param {String} id The id of the dragged element
32125                  * @method afterDragOut
32126                  */
32127                 this.afterDragOut(target, e, id);
32128             }
32129         }
32130         this.cachedTarget = null;
32131     },
32132
32133     /**
32134      * An empty function by default, but provided so that you can perform a custom action before the dragged
32135      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
32136      * @param {Ext.dd.DragDrop} target The drop target
32137      * @param {Event} e The event object
32138      * @param {String} id The id of the dragged element
32139      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32140      */
32141     beforeDragOut : function(target, e, id){
32142         return true;
32143     },
32144     
32145     // private
32146     onDragDrop : function(e, id){
32147         var target = this.cachedTarget || Ext.dd.DragDropMgr.getDDById(id);
32148         if(this.beforeDragDrop(target, e, id) !== false){
32149             if(target.isNotifyTarget){
32150                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
32151                     this.onValidDrop(target, e, id);
32152                 }else{
32153                     this.onInvalidDrop(target, e, id);
32154                 }
32155             }else{
32156                 this.onValidDrop(target, e, id);
32157             }
32158             
32159             if(this.afterDragDrop){
32160                 /**
32161                  * An empty function by default, but provided so that you can perform a custom action
32162                  * after a valid drag drop has occurred by providing an implementation.
32163                  * @param {Ext.dd.DragDrop} target The drop target
32164                  * @param {Event} e The event object
32165                  * @param {String} id The id of the dropped element
32166                  * @method afterDragDrop
32167                  */
32168                 this.afterDragDrop(target, e, id);
32169             }
32170         }
32171         delete this.cachedTarget;
32172     },
32173
32174     /**
32175      * An empty function by default, but provided so that you can perform a custom action before the dragged
32176      * item is dropped onto the target and optionally cancel the onDragDrop.
32177      * @param {Ext.dd.DragDrop} target The drop target
32178      * @param {Event} e The event object
32179      * @param {String} id The id of the dragged element
32180      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
32181      */
32182     beforeDragDrop : function(target, e, id){
32183         return true;
32184     },
32185
32186     // private
32187     onValidDrop : function(target, e, id){
32188         this.hideProxy();
32189         if(this.afterValidDrop){
32190             /**
32191              * An empty function by default, but provided so that you can perform a custom action
32192              * after a valid drop has occurred by providing an implementation.
32193              * @param {Object} target The target DD 
32194              * @param {Event} e The event object
32195              * @param {String} id The id of the dropped element
32196              * @method afterInvalidDrop
32197              */
32198             this.afterValidDrop(target, e, id);
32199         }
32200     },
32201
32202     // private
32203     getRepairXY : function(e, data){
32204         return this.el.getXY();  
32205     },
32206
32207     // private
32208     onInvalidDrop : function(target, e, id){
32209         this.beforeInvalidDrop(target, e, id);
32210         if(this.cachedTarget){
32211             if(this.cachedTarget.isNotifyTarget){
32212                 this.cachedTarget.notifyOut(this, e, this.dragData);
32213             }
32214             this.cacheTarget = null;
32215         }
32216         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
32217
32218         if(this.afterInvalidDrop){
32219             /**
32220              * An empty function by default, but provided so that you can perform a custom action
32221              * after an invalid drop has occurred by providing an implementation.
32222              * @param {Event} e The event object
32223              * @param {String} id The id of the dropped element
32224              * @method afterInvalidDrop
32225              */
32226             this.afterInvalidDrop(e, id);
32227         }
32228     },
32229
32230     // private
32231     afterRepair : function(){
32232         if(Ext.enableFx){
32233             this.el.highlight(this.hlColor || "c3daf9");
32234         }
32235         this.dragging = false;
32236     },
32237
32238     /**
32239      * An empty function by default, but provided so that you can perform a custom action after an invalid
32240      * drop has occurred.
32241      * @param {Ext.dd.DragDrop} target The drop target
32242      * @param {Event} e The event object
32243      * @param {String} id The id of the dragged element
32244      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
32245      */
32246     beforeInvalidDrop : function(target, e, id){
32247         return true;
32248     },
32249
32250     // private
32251     handleMouseDown : function(e){
32252         if(this.dragging) {
32253             return;
32254         }
32255         var data = this.getDragData(e);
32256         if(data && this.onBeforeDrag(data, e) !== false){
32257             this.dragData = data;
32258             this.proxy.stop();
32259             Ext.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
32260         } 
32261     },
32262
32263     /**
32264      * An empty function by default, but provided so that you can perform a custom action before the initial
32265      * drag event begins and optionally cancel it.
32266      * @param {Object} data An object containing arbitrary data to be shared with drop targets
32267      * @param {Event} e The event object
32268      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
32269      */
32270     onBeforeDrag : function(data, e){
32271         return true;
32272     },
32273
32274     /**
32275      * An empty function by default, but provided so that you can perform a custom action once the initial
32276      * drag event has begun.  The drag cannot be canceled from this function.
32277      * @param {Number} x The x position of the click on the dragged object
32278      * @param {Number} y The y position of the click on the dragged object
32279      */
32280     onStartDrag : Ext.emptyFn,
32281
32282     // private override
32283     startDrag : function(x, y){
32284         this.proxy.reset();
32285         this.dragging = true;
32286         this.proxy.update("");
32287         this.onInitDrag(x, y);
32288         this.proxy.show();
32289     },
32290
32291     // private
32292     onInitDrag : function(x, y){
32293         var clone = this.el.dom.cloneNode(true);
32294         clone.id = Ext.id(); // prevent duplicate ids
32295         this.proxy.update(clone);
32296         this.onStartDrag(x, y);
32297         return true;
32298     },
32299
32300     /**
32301      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}
32302      * @return {Ext.dd.StatusProxy} proxy The StatusProxy
32303      */
32304     getProxy : function(){
32305         return this.proxy;  
32306     },
32307
32308     /**
32309      * Hides the drag source's {@link Ext.dd.StatusProxy}
32310      */
32311     hideProxy : function(){
32312         this.proxy.hide();  
32313         this.proxy.reset(true);
32314         this.dragging = false;
32315     },
32316
32317     // private
32318     triggerCacheRefresh : function(){
32319         Ext.dd.DDM.refreshCache(this.groups);
32320     },
32321
32322     // private - override to prevent hiding
32323     b4EndDrag: function(e) {
32324     },
32325
32326     // private - override to prevent moving
32327     endDrag : function(e){
32328         this.onEndDrag(this.dragData, e);
32329     },
32330
32331     // private
32332     onEndDrag : function(data, e){
32333     },
32334     
32335     // private - pin to cursor
32336     autoOffset : function(x, y) {
32337         this.setDelta(-12, -20);
32338     },
32339     
32340     destroy: function(){
32341         Ext.dd.DragSource.superclass.destroy.call(this);
32342         Ext.destroy(this.proxy);
32343     }
32344 });/**
32345  * @class Ext.dd.DropTarget
32346  * @extends Ext.dd.DDTarget
32347  * A simple class that provides the basic implementation needed to make any element a drop target that can have
32348  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
32349  * @constructor
32350  * @param {Mixed} el The container element
32351  * @param {Object} config
32352  */
32353 Ext.dd.DropTarget = function(el, config){
32354     this.el = Ext.get(el);
32355     
32356     Ext.apply(this, config);
32357     
32358     if(this.containerScroll){
32359         Ext.dd.ScrollManager.register(this.el);
32360     }
32361     
32362     Ext.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
32363           {isTarget: true});
32364
32365 };
32366
32367 Ext.extend(Ext.dd.DropTarget, Ext.dd.DDTarget, {
32368     /**
32369      * @cfg {String} ddGroup
32370      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
32371      * interact with other drag drop objects in the same group (defaults to undefined).
32372      */
32373     /**
32374      * @cfg {String} overClass
32375      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
32376      */
32377     /**
32378      * @cfg {String} dropAllowed
32379      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
32380      */
32381     dropAllowed : "x-dd-drop-ok",
32382     /**
32383      * @cfg {String} dropNotAllowed
32384      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
32385      */
32386     dropNotAllowed : "x-dd-drop-nodrop",
32387
32388     // private
32389     isTarget : true,
32390
32391     // private
32392     isNotifyTarget : true,
32393
32394     /**
32395      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the
32396      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
32397      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
32398      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32399      * @param {Event} e The event
32400      * @param {Object} data An object containing arbitrary data supplied by the drag source
32401      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32402      * underlying {@link Ext.dd.StatusProxy} can be updated
32403      */
32404     notifyEnter : function(dd, e, data){
32405         if(this.overClass){
32406             this.el.addClass(this.overClass);
32407         }
32408         return this.dropAllowed;
32409     },
32410
32411     /**
32412      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.
32413      * This method will be called on every mouse movement while the drag source is over the drop target.
32414      * This default implementation simply returns the dropAllowed config value.
32415      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32416      * @param {Event} e The event
32417      * @param {Object} data An object containing arbitrary data supplied by the drag source
32418      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32419      * underlying {@link Ext.dd.StatusProxy} can be updated
32420      */
32421     notifyOver : function(dd, e, data){
32422         return this.dropAllowed;
32423     },
32424
32425     /**
32426      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged
32427      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
32428      * overClass (if any) from the drop element.
32429      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32430      * @param {Event} e The event
32431      * @param {Object} data An object containing arbitrary data supplied by the drag source
32432      */
32433     notifyOut : function(dd, e, data){
32434         if(this.overClass){
32435             this.el.removeClass(this.overClass);
32436         }
32437     },
32438
32439     /**
32440      * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has
32441      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
32442      * implementation that does something to process the drop event and returns true so that the drag source's
32443      * repair action does not run.
32444      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32445      * @param {Event} e The event
32446      * @param {Object} data An object containing arbitrary data supplied by the drag source
32447      * @return {Boolean} True if the drop was valid, else false
32448      */
32449     notifyDrop : function(dd, e, data){
32450         return false;
32451     }
32452 });/**
32453  * @class Ext.dd.DragZone
32454  * @extends Ext.dd.DragSource
32455  * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>
32456  * <p>This class does not move the drag target nodes, but a proxy element which may contain
32457  * any DOM structure you wish. The DOM element to show in the proxy is provided by either a
32458  * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>
32459  * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some
32460  * application object (For example nodes in a {@link Ext.DataView DataView}) then use of this class
32461  * is the most efficient way to "activate" those nodes.</p>
32462  * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.
32463  * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure
32464  * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed
32465  * mouse event to see if it has taken place within an element, or class of elements. This is easily done
32466  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
32467  * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following
32468  * technique. Knowledge of the use of the DataView is required:</p><pre><code>
32469 myDataView.on('render', function(v) {
32470     myDataView.dragZone = new Ext.dd.DragZone(v.getEl(), {
32471
32472 //      On receipt of a mousedown event, see if it is within a DataView node.
32473 //      Return a drag data object if so.
32474         getDragData: function(e) {
32475
32476 //          Use the DataView's own itemSelector (a mandatory property) to
32477 //          test if the mousedown is within one of the DataView's nodes.
32478             var sourceEl = e.getTarget(v.itemSelector, 10);
32479
32480 //          If the mousedown is within a DataView node, clone the node to produce
32481 //          a ddel element for use by the drag proxy. Also add application data
32482 //          to the returned data object.
32483             if (sourceEl) {
32484                 d = sourceEl.cloneNode(true);
32485                 d.id = Ext.id();
32486                 return {
32487                     ddel: d,
32488                     sourceEl: sourceEl,
32489                     repairXY: Ext.fly(sourceEl).getXY(),
32490                     sourceStore: v.store,
32491                     draggedRecord: v.{@link Ext.DataView#getRecord getRecord}(sourceEl)
32492                 }
32493             }
32494         },
32495
32496 //      Provide coordinates for the proxy to slide back to on failed drag.
32497 //      This is the original XY coordinates of the draggable element captured
32498 //      in the getDragData method.
32499         getRepairXY: function() {
32500             return this.dragData.repairXY;
32501         }
32502     });
32503 });</code></pre>
32504  * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which
32505  * cooperates with this DragZone.
32506  * @constructor
32507  * @param {Mixed} el The container element
32508  * @param {Object} config
32509  */
32510 Ext.dd.DragZone = function(el, config){
32511     Ext.dd.DragZone.superclass.constructor.call(this, el, config);
32512     if(this.containerScroll){
32513         Ext.dd.ScrollManager.register(this.el);
32514     }
32515 };
32516
32517 Ext.extend(Ext.dd.DragZone, Ext.dd.DragSource, {
32518     /**
32519      * This property contains the data representing the dragged object. This data is set up by the implementation
32520      * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain
32521      * any other data according to the application's needs.
32522      * @type Object
32523      * @property dragData
32524      */
32525     /**
32526      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
32527      * for auto scrolling during drag operations.
32528      */
32529     /**
32530      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
32531      * method after a failed drop (defaults to "c3daf9" - light blue)
32532      */
32533
32534     /**
32535      * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}
32536      * for a valid target to drag based on the mouse down. Override this method
32537      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
32538      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
32539      * @param {EventObject} e The mouse down event
32540      * @return {Object} The dragData
32541      */
32542     getDragData : function(e){
32543         return Ext.dd.Registry.getHandleFromEvent(e);
32544     },
32545     
32546     /**
32547      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
32548      * this.dragData.ddel
32549      * @param {Number} x The x position of the click on the dragged object
32550      * @param {Number} y The y position of the click on the dragged object
32551      * @return {Boolean} true to continue the drag, false to cancel
32552      */
32553     onInitDrag : function(x, y){
32554         this.proxy.update(this.dragData.ddel.cloneNode(true));
32555         this.onStartDrag(x, y);
32556         return true;
32557     },
32558     
32559     /**
32560      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
32561      */
32562     afterRepair : function(){
32563         if(Ext.enableFx){
32564             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
32565         }
32566         this.dragging = false;
32567     },
32568
32569     /**
32570      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
32571      * the XY of this.dragData.ddel
32572      * @param {EventObject} e The mouse up event
32573      * @return {Array} The xy location (e.g. [100, 200])
32574      */
32575     getRepairXY : function(e){
32576         return Ext.Element.fly(this.dragData.ddel).getXY();  
32577     }
32578 });/**
32579  * @class Ext.dd.DropZone
32580  * @extends Ext.dd.DropTarget
32581  * <p>This class provides a container DD instance that allows dropping on multiple child target nodes.</p>
32582  * <p>By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.
32583  * However a simpler way to allow a DropZone to manage any number of target elements is to configure the
32584  * DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed
32585  * mouse event to see if it has taken place within an element, or class of elements. This is easily done
32586  * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
32587  * {@link Ext.DomQuery} selector.</p>
32588  * <p>Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over
32589  * a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},
32590  * {@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations
32591  * of these methods to provide application-specific behaviour for these events to update both
32592  * application state, and UI state.</p>
32593  * <p>For example to make a GridPanel a cooperating target with the example illustrated in
32594  * {@link Ext.dd.DragZone DragZone}, the following technique might be used:</p><pre><code>
32595 myGridPanel.on('render', function() {
32596     myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {
32597
32598 //      If the mouse is over a grid row, return that node. This is
32599 //      provided as the "target" parameter in all "onNodeXXXX" node event handling functions
32600         getTargetFromEvent: function(e) {
32601             return e.getTarget(myGridPanel.getView().rowSelector);
32602         },
32603
32604 //      On entry into a target node, highlight that node.
32605         onNodeEnter : function(target, dd, e, data){ 
32606             Ext.fly(target).addClass('my-row-highlight-class');
32607         },
32608
32609 //      On exit from a target node, unhighlight that node.
32610         onNodeOut : function(target, dd, e, data){ 
32611             Ext.fly(target).removeClass('my-row-highlight-class');
32612         },
32613
32614 //      While over a target node, return the default drop allowed class which
32615 //      places a "tick" icon into the drag proxy.
32616         onNodeOver : function(target, dd, e, data){ 
32617             return Ext.dd.DropZone.prototype.dropAllowed;
32618         },
32619
32620 //      On node drop we can interrogate the target to find the underlying
32621 //      application object that is the real target of the dragged data.
32622 //      In this case, it is a Record in the GridPanel's Store.
32623 //      We can use the data set up by the DragZone's getDragData method to read
32624 //      any data we decided to attach in the DragZone's getDragData method.
32625         onNodeDrop : function(target, dd, e, data){
32626             var rowIndex = myGridPanel.getView().findRowIndex(target);
32627             var r = myGridPanel.getStore().getAt(rowIndex);
32628             Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +
32629                 ' on Record id ' + r.id);
32630             return true;
32631         }
32632     });
32633 }
32634 </code></pre>
32635  * See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which
32636  * cooperates with this DropZone.
32637  * @constructor
32638  * @param {Mixed} el The container element
32639  * @param {Object} config
32640  */
32641 Ext.dd.DropZone = function(el, config){
32642     Ext.dd.DropZone.superclass.constructor.call(this, el, config);
32643 };
32644
32645 Ext.extend(Ext.dd.DropZone, Ext.dd.DropTarget, {
32646     /**
32647      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
32648      * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to
32649      * provide your own custom lookup.
32650      * @param {Event} e The event
32651      * @return {Object} data The custom data
32652      */
32653     getTargetFromEvent : function(e){
32654         return Ext.dd.Registry.getTargetFromEvent(e);
32655     },
32656
32657     /**
32658      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node
32659      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
32660      * This method has no default implementation and should be overridden to provide
32661      * node-specific processing if necessary.
32662      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
32663      * {@link #getTargetFromEvent} for this node)
32664      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32665      * @param {Event} e The event
32666      * @param {Object} data An object containing arbitrary data supplied by the drag source
32667      */
32668     onNodeEnter : function(n, dd, e, data){
32669         
32670     },
32671
32672     /**
32673      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node
32674      * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
32675      * The default implementation returns this.dropNotAllowed, so it should be
32676      * overridden to provide the proper feedback.
32677      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
32678      * {@link #getTargetFromEvent} for this node)
32679      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32680      * @param {Event} e The event
32681      * @param {Object} data An object containing arbitrary data supplied by the drag source
32682      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32683      * underlying {@link Ext.dd.StatusProxy} can be updated
32684      */
32685     onNodeOver : function(n, dd, e, data){
32686         return this.dropAllowed;
32687     },
32688
32689     /**
32690      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of
32691      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
32692      * node-specific processing if necessary.
32693      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
32694      * {@link #getTargetFromEvent} for this node)
32695      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32696      * @param {Event} e The event
32697      * @param {Object} data An object containing arbitrary data supplied by the drag source
32698      */
32699     onNodeOut : function(n, dd, e, data){
32700         
32701     },
32702
32703     /**
32704      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto
32705      * the drop node.  The default implementation returns false, so it should be overridden to provide the
32706      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
32707      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
32708      * {@link #getTargetFromEvent} for this node)
32709      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32710      * @param {Event} e The event
32711      * @param {Object} data An object containing arbitrary data supplied by the drag source
32712      * @return {Boolean} True if the drop was valid, else false
32713      */
32714     onNodeDrop : function(n, dd, e, data){
32715         return false;
32716     },
32717
32718     /**
32719      * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,
32720      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
32721      * it should be overridden to provide the proper feedback if necessary.
32722      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32723      * @param {Event} e The event
32724      * @param {Object} data An object containing arbitrary data supplied by the drag source
32725      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32726      * underlying {@link Ext.dd.StatusProxy} can be updated
32727      */
32728     onContainerOver : function(dd, e, data){
32729         return this.dropNotAllowed;
32730     },
32731
32732     /**
32733      * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,
32734      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
32735      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
32736      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
32737      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32738      * @param {Event} e The event
32739      * @param {Object} data An object containing arbitrary data supplied by the drag source
32740      * @return {Boolean} True if the drop was valid, else false
32741      */
32742     onContainerDrop : function(dd, e, data){
32743         return false;
32744     },
32745
32746     /**
32747      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over
32748      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
32749      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
32750      * you should override this method and provide a custom implementation.
32751      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32752      * @param {Event} e The event
32753      * @param {Object} data An object containing arbitrary data supplied by the drag source
32754      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32755      * underlying {@link Ext.dd.StatusProxy} can be updated
32756      */
32757     notifyEnter : function(dd, e, data){
32758         return this.dropNotAllowed;
32759     },
32760
32761     /**
32762      * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.
32763      * This method will be called on every mouse movement while the drag source is over the drop zone.
32764      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
32765      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
32766      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
32767      * registered node, it will call {@link #onContainerOver}.
32768      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32769      * @param {Event} e The event
32770      * @param {Object} data An object containing arbitrary data supplied by the drag source
32771      * @return {String} status The CSS class that communicates the drop status back to the source so that the
32772      * underlying {@link Ext.dd.StatusProxy} can be updated
32773      */
32774     notifyOver : function(dd, e, data){
32775         var n = this.getTargetFromEvent(e);
32776         if(!n){ // not over valid drop target
32777             if(this.lastOverNode){
32778                 this.onNodeOut(this.lastOverNode, dd, e, data);
32779                 this.lastOverNode = null;
32780             }
32781             return this.onContainerOver(dd, e, data);
32782         }
32783         if(this.lastOverNode != n){
32784             if(this.lastOverNode){
32785                 this.onNodeOut(this.lastOverNode, dd, e, data);
32786             }
32787             this.onNodeEnter(n, dd, e, data);
32788             this.lastOverNode = n;
32789         }
32790         return this.onNodeOver(n, dd, e, data);
32791     },
32792
32793     /**
32794      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged
32795      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
32796      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
32797      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
32798      * @param {Event} e The event
32799      * @param {Object} data An object containing arbitrary data supplied by the drag zone
32800      */
32801     notifyOut : function(dd, e, data){
32802         if(this.lastOverNode){
32803             this.onNodeOut(this.lastOverNode, dd, e, data);
32804             this.lastOverNode = null;
32805         }
32806     },
32807
32808     /**
32809      * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has
32810      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
32811      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
32812      * otherwise it will call {@link #onContainerDrop}.
32813      * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
32814      * @param {Event} e The event
32815      * @param {Object} data An object containing arbitrary data supplied by the drag source
32816      * @return {Boolean} True if the drop was valid, else false
32817      */
32818     notifyDrop : function(dd, e, data){
32819         if(this.lastOverNode){
32820             this.onNodeOut(this.lastOverNode, dd, e, data);
32821             this.lastOverNode = null;
32822         }
32823         var n = this.getTargetFromEvent(e);
32824         return n ?
32825             this.onNodeDrop(n, dd, e, data) :
32826             this.onContainerDrop(dd, e, data);
32827     },
32828
32829     // private
32830     triggerCacheRefresh : function(){
32831         Ext.dd.DDM.refreshCache(this.groups);
32832     }  
32833 });/**
32834  * @class Ext.Element
32835  */
32836 Ext.Element.addMethods({
32837     /**
32838      * Initializes a {@link Ext.dd.DD} drag drop object for this element.
32839      * @param {String} group The group the DD object is member of
32840      * @param {Object} config The DD config object
32841      * @param {Object} overrides An object containing methods to override/implement on the DD object
32842      * @return {Ext.dd.DD} The DD object
32843      */
32844     initDD : function(group, config, overrides){
32845         var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);
32846         return Ext.apply(dd, overrides);
32847     },
32848
32849     /**
32850      * Initializes a {@link Ext.dd.DDProxy} object for this element.
32851      * @param {String} group The group the DDProxy object is member of
32852      * @param {Object} config The DDProxy config object
32853      * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
32854      * @return {Ext.dd.DDProxy} The DDProxy object
32855      */
32856     initDDProxy : function(group, config, overrides){
32857         var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);
32858         return Ext.apply(dd, overrides);
32859     },
32860
32861     /**
32862      * Initializes a {@link Ext.dd.DDTarget} object for this element.
32863      * @param {String} group The group the DDTarget object is member of
32864      * @param {Object} config The DDTarget config object
32865      * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
32866      * @return {Ext.dd.DDTarget} The DDTarget object
32867      */
32868     initDDTarget : function(group, config, overrides){
32869         var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);
32870         return Ext.apply(dd, overrides);
32871     }
32872 });
32873 /**
32874  * @class Ext.data.Api
32875  * @extends Object
32876  * Ext.data.Api is a singleton designed to manage the data API including methods
32877  * for validating a developer's DataProxy API.  Defines variables for CRUD actions
32878  * create, read, update and destroy in addition to a mapping of RESTful HTTP methods
32879  * GET, POST, PUT and DELETE to CRUD actions.
32880  * @singleton
32881  */
32882 Ext.data.Api = (function() {
32883
32884     // private validActions.  validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
32885     // Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
32886     // For efficiency, some methods will first check this hash for a match.  Those methods which do acces validActions will cache their result here.
32887     // We cannot pre-define this hash since the developer may over-ride the actions at runtime.
32888     var validActions = {};
32889
32890     return {
32891         /**
32892          * Defined actions corresponding to remote actions:
32893          * <pre><code>
32894 actions: {
32895     create  : 'create',  // Text representing the remote-action to create records on server.
32896     read    : 'read',    // Text representing the remote-action to read/load data from server.
32897     update  : 'update',  // Text representing the remote-action to update records on server.
32898     destroy : 'destroy'  // Text representing the remote-action to destroy records on server.
32899 }
32900          * </code></pre>
32901          * @property actions
32902          * @type Object
32903          */
32904         actions : {
32905             create  : 'create',
32906             read    : 'read',
32907             update  : 'update',
32908             destroy : 'destroy'
32909         },
32910
32911         /**
32912          * Defined {CRUD action}:{HTTP method} pairs to associate HTTP methods with the
32913          * corresponding actions for {@link Ext.data.DataProxy#restful RESTful proxies}.
32914          * Defaults to:
32915          * <pre><code>
32916 restActions : {
32917     create  : 'POST',
32918     read    : 'GET',
32919     update  : 'PUT',
32920     destroy : 'DELETE'
32921 },
32922          * </code></pre>
32923          */
32924         restActions : {
32925             create  : 'POST',
32926             read    : 'GET',
32927             update  : 'PUT',
32928             destroy : 'DELETE'
32929         },
32930
32931         /**
32932          * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
32933          * @param {String} action Action to test for availability.
32934          * @return {Boolean}
32935          */
32936         isAction : function(action) {
32937             return (Ext.data.Api.actions[action]) ? true : false;
32938         },
32939
32940         /**
32941          * 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
32942          * 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
32943          * 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
32944          * required.  This method will cache discovered KEYS into the private validActions hash.
32945          * @param {String} name The runtime name of the action.
32946          * @return {String||null} returns the action-key, or verb of the user-action or null if invalid.
32947          * @nodoc
32948          */
32949         getVerb : function(name) {
32950             if (validActions[name]) {
32951                 return validActions[name];  // <-- found in cache.  return immediately.
32952             }
32953             for (var verb in this.actions) {
32954                 if (this.actions[verb] === name) {
32955                     validActions[name] = verb;
32956                     break;
32957                 }
32958             }
32959             return (validActions[name] !== undefined) ? validActions[name] : null;
32960         },
32961
32962         /**
32963          * Returns true if the supplied API is valid; that is, check that all keys match defined actions
32964          * otherwise returns an array of mistakes.
32965          * @return {String[]|true}
32966          */
32967         isValid : function(api){
32968             var invalid = [];
32969             var crud = this.actions; // <-- cache a copy of the actions.
32970             for (var action in api) {
32971                 if (!(action in crud)) {
32972                     invalid.push(action);
32973                 }
32974             }
32975             return (!invalid.length) ? true : invalid;
32976         },
32977
32978         /**
32979          * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions
32980          * point to the same url.  The question is important for deciding whether to insert the "xaction" HTTP parameter within an
32981          * Ajax request.  This method is used internally and shouldn't generally need to be called directly.
32982          * @param {Ext.data.DataProxy} proxy
32983          * @param {String} verb
32984          * @return {Boolean}
32985          */
32986         hasUniqueUrl : function(proxy, verb) {
32987             var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
32988             var unique = true;
32989             for (var action in proxy.api) {
32990                 if ((unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false) {
32991                     break;
32992                 }
32993             }
32994             return unique;
32995         },
32996
32997         /**
32998          * This method is used internally by <tt>{@link Ext.data.DataProxy DataProxy}</tt> and should not generally need to be used directly.
32999          * Each action of a DataProxy api can be initially defined as either a String or an Object.  When specified as an object,
33000          * one can explicitly define the HTTP method (GET|POST) to use for each CRUD action.  This method will prepare the supplied API, setting
33001          * each action to the Object form.  If your API-actions do not explicitly define the HTTP method, the "method" configuration-parameter will
33002          * be used.  If the method configuration parameter is not specified, POST will be used.
33003          <pre><code>
33004 new Ext.data.HttpProxy({
33005     method: "POST",     // <-- default HTTP method when not specified.
33006     api: {
33007         create: 'create.php',
33008         load: 'read.php',
33009         save: 'save.php',
33010         destroy: 'destroy.php'
33011     }
33012 });
33013
33014 // Alternatively, one can use the object-form to specify the API
33015 new Ext.data.HttpProxy({
33016     api: {
33017         load: {url: 'read.php', method: 'GET'},
33018         create: 'create.php',
33019         destroy: 'destroy.php',
33020         save: 'update.php'
33021     }
33022 });
33023         </code></pre>
33024          *
33025          * @param {Ext.data.DataProxy} proxy
33026          */
33027         prepare : function(proxy) {
33028             if (!proxy.api) {
33029                 proxy.api = {}; // <-- No api?  create a blank one.
33030             }
33031             for (var verb in this.actions) {
33032                 var action = this.actions[verb];
33033                 proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
33034                 if (typeof(proxy.api[action]) == 'string') {
33035                     proxy.api[action] = {
33036                         url: proxy.api[action],
33037                         method: (proxy.restful === true) ? Ext.data.Api.restActions[action] : undefined
33038                     };
33039                 }
33040             }
33041         },
33042
33043         /**
33044          * Prepares a supplied Proxy to be RESTful.  Sets the HTTP method for each api-action to be one of
33045          * GET, POST, PUT, DELETE according to the defined {@link #restActions}.
33046          * @param {Ext.data.DataProxy} proxy
33047          */
33048         restify : function(proxy) {
33049             proxy.restful = true;
33050             for (var verb in this.restActions) {
33051                 proxy.api[this.actions[verb]].method ||
33052                     (proxy.api[this.actions[verb]].method = this.restActions[verb]);
33053             }
33054             // TODO: perhaps move this interceptor elsewhere?  like into DataProxy, perhaps?  Placed here
33055             // to satisfy initial 3.0 final release of REST features.
33056             proxy.onWrite = proxy.onWrite.createInterceptor(function(action, o, response, rs) {
33057                 var reader = o.reader;
33058                 var res = new Ext.data.Response({
33059                     action: action,
33060                     raw: response
33061                 });
33062
33063                 switch (response.status) {
33064                     case 200:   // standard 200 response, send control back to HttpProxy#onWrite by returning true from this intercepted #onWrite
33065                         return true;
33066                         break;
33067                     case 201:   // entity created but no response returned
33068                         if (Ext.isEmpty(res.raw.responseText)) {
33069                           res.success = true;
33070                         } else {
33071                           //if the response contains data, treat it like a 200
33072                           return true;
33073                         }
33074                         break;
33075                     case 204:  // no-content.  Create a fake response.
33076                         res.success = true;
33077                         res.data = null;
33078                         break;
33079                     default:
33080                         return true;
33081                         break;
33082                 }
33083                 if (res.success === true) {
33084                     this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
33085                 } else {
33086                     this.fireEvent('exception', this, 'remote', action, o, res, rs);
33087                 }
33088                 o.request.callback.call(o.request.scope, res.data, res, res.success);
33089
33090                 return false;   // <-- false to prevent intercepted function from running.
33091             }, proxy);
33092         }
33093     };
33094 })();
33095
33096 /**
33097  * Ext.data.Response
33098  * Experimental.  Do not use directly.
33099  */
33100 Ext.data.Response = function(params, response) {
33101     Ext.apply(this, params, {
33102         raw: response
33103     });
33104 };
33105 Ext.data.Response.prototype = {
33106     message : null,
33107     success : false,
33108     status : null,
33109     root : null,
33110     raw : null,
33111
33112     getMessage : function() {
33113         return this.message;
33114     },
33115     getSuccess : function() {
33116         return this.success;
33117     },
33118     getStatus : function() {
33119         return this.status;
33120     },
33121     getRoot : function() {
33122         return this.root;
33123     },
33124     getRawResponse : function() {
33125         return this.raw;
33126     }
33127 };
33128
33129 /**
33130  * @class Ext.data.Api.Error
33131  * @extends Ext.Error
33132  * Error class for Ext.data.Api errors
33133  */
33134 Ext.data.Api.Error = Ext.extend(Ext.Error, {
33135     constructor : function(message, arg) {
33136         this.arg = arg;
33137         Ext.Error.call(this, message);
33138     },
33139     name: 'Ext.data.Api'
33140 });
33141 Ext.apply(Ext.data.Api.Error.prototype, {
33142     lang: {
33143         '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.',
33144         'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
33145         'invalid-url': 'Invalid url.  Please review your proxy configuration.',
33146         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
33147     }
33148 });
33149
33150
33151
33152 /**
33153  * @class Ext.data.SortTypes
33154  * @singleton
33155  * Defines the default sorting (casting?) comparison functions used when sorting data.
33156  */
33157 Ext.data.SortTypes = {
33158     /**
33159      * Default sort that does nothing
33160      * @param {Mixed} s The value being converted
33161      * @return {Mixed} The comparison value
33162      */
33163     none : function(s){
33164         return s;
33165     },
33166     
33167     /**
33168      * The regular expression used to strip tags
33169      * @type {RegExp}
33170      * @property
33171      */
33172     stripTagsRE : /<\/?[^>]+>/gi,
33173     
33174     /**
33175      * Strips all HTML tags to sort on text only
33176      * @param {Mixed} s The value being converted
33177      * @return {String} The comparison value
33178      */
33179     asText : function(s){
33180         return String(s).replace(this.stripTagsRE, "");
33181     },
33182     
33183     /**
33184      * Strips all HTML tags to sort on text only - Case insensitive
33185      * @param {Mixed} s The value being converted
33186      * @return {String} The comparison value
33187      */
33188     asUCText : function(s){
33189         return String(s).toUpperCase().replace(this.stripTagsRE, "");
33190     },
33191     
33192     /**
33193      * Case insensitive string
33194      * @param {Mixed} s The value being converted
33195      * @return {String} The comparison value
33196      */
33197     asUCString : function(s) {
33198         return String(s).toUpperCase();
33199     },
33200     
33201     /**
33202      * Date sorting
33203      * @param {Mixed} s The value being converted
33204      * @return {Number} The comparison value
33205      */
33206     asDate : function(s) {
33207         if(!s){
33208             return 0;
33209         }
33210         if(Ext.isDate(s)){
33211             return s.getTime();
33212         }
33213         return Date.parse(String(s));
33214     },
33215     
33216     /**
33217      * Float sorting
33218      * @param {Mixed} s The value being converted
33219      * @return {Float} The comparison value
33220      */
33221     asFloat : function(s) {
33222         var val = parseFloat(String(s).replace(/,/g, ""));
33223         return isNaN(val) ? 0 : val;
33224     },
33225     
33226     /**
33227      * Integer sorting
33228      * @param {Mixed} s The value being converted
33229      * @return {Number} The comparison value
33230      */
33231     asInt : function(s) {
33232         var val = parseInt(String(s).replace(/,/g, ""), 10);
33233         return isNaN(val) ? 0 : val;
33234     }
33235 };/**
33236  * @class Ext.data.Record
33237  * <p>Instances of this class encapsulate both Record <em>definition</em> information, and Record
33238  * <em>value</em> information for use in {@link Ext.data.Store} objects, or any code which needs
33239  * to access Records cached in an {@link Ext.data.Store} object.</p>
33240  * <p>Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
33241  * Instances are usually only created by {@link Ext.data.Reader} implementations when processing unformatted data
33242  * objects.</p>
33243  * <p>Note that an instance of a Record class may only belong to one {@link Ext.data.Store Store} at a time.
33244  * In order to copy data from one Store to another, use the {@link #copy} method to create an exact
33245  * copy of the Record, and insert the new instance into the other Store.</p>
33246  * <p>When serializing a Record for submission to the server, be aware that it contains many private
33247  * properties, and also a reference to its owning Store which in turn holds references to its Records.
33248  * This means that a whole Record may not be encoded using {@link Ext.util.JSON.encode}. Instead, use the
33249  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
33250  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
33251  * @constructor
33252  * <p>This constructor should not be used to create Record objects. Instead, use {@link #create} to
33253  * generate a subclass of Ext.data.Record configured with information about its constituent fields.<p>
33254  * <p><b>The generated constructor has the same signature as this constructor.</b></p>
33255  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
33256  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
33257  * for each field will be assigned.
33258  * @param {Object} id (Optional) The id of the Record. The id is used by the
33259  * {@link Ext.data.Store} object which owns the Record to index its collection
33260  * of Records (therefore this id should be unique within each store). If an
33261  * <code>id</code> is not specified a <b><code>{@link #phantom}</code></b>
33262  * Record will be created with an {@link #Record.id automatically generated id}.
33263  */
33264 Ext.data.Record = function(data, id){
33265     // if no id, call the auto id method
33266     this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
33267     this.data = data || {};
33268 };
33269
33270 /**
33271  * Generate a constructor for a specific Record layout.
33272  * @param {Array} o An Array of <b>{@link Ext.data.Field Field}</b> definition objects.
33273  * The constructor generated by this method may be used to create new Record instances. The data
33274  * object must contain properties named after the {@link Ext.data.Field field}
33275  * <b><tt>{@link Ext.data.Field#name}s</tt></b>.  Example usage:<pre><code>
33276 // create a Record constructor from a description of the fields
33277 var TopicRecord = Ext.data.Record.create([ // creates a subclass of Ext.data.Record
33278     {{@link Ext.data.Field#name name}: 'title', {@link Ext.data.Field#mapping mapping}: 'topic_title'},
33279     {name: 'author', mapping: 'username', allowBlank: false},
33280     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
33281     {name: 'lastPost', mapping: 'post_time', type: 'date'},
33282     {name: 'lastPoster', mapping: 'user2'},
33283     {name: 'excerpt', mapping: 'post_text', allowBlank: false},
33284     // In the simplest case, if no properties other than <tt>name</tt> are required,
33285     // a field definition may consist of just a String for the field name.
33286     'signature'
33287 ]);
33288
33289 // create Record instance
33290 var myNewRecord = new TopicRecord(
33291     {
33292         title: 'Do my job please',
33293         author: 'noobie',
33294         totalPosts: 1,
33295         lastPost: new Date(),
33296         lastPoster: 'Animal',
33297         excerpt: 'No way dude!',
33298         signature: ''
33299     },
33300     id // optionally specify the id of the record otherwise {@link #Record.id one is auto-assigned}
33301 );
33302 myStore.{@link Ext.data.Store#add add}(myNewRecord);
33303 </code></pre>
33304  * @method create
33305  * @return {Function} A constructor which is used to create new Records according
33306  * to the definition. The constructor has the same signature as {@link #Record}.
33307  * @static
33308  */
33309 Ext.data.Record.create = function(o){
33310     var f = Ext.extend(Ext.data.Record, {});
33311     var p = f.prototype;
33312     p.fields = new Ext.util.MixedCollection(false, function(field){
33313         return field.name;
33314     });
33315     for(var i = 0, len = o.length; i < len; i++){
33316         p.fields.add(new Ext.data.Field(o[i]));
33317     }
33318     f.getField = function(name){
33319         return p.fields.get(name);
33320     };
33321     return f;
33322 };
33323
33324 Ext.data.Record.PREFIX = 'ext-record';
33325 Ext.data.Record.AUTO_ID = 1;
33326 Ext.data.Record.EDIT = 'edit';
33327 Ext.data.Record.REJECT = 'reject';
33328 Ext.data.Record.COMMIT = 'commit';
33329
33330
33331 /**
33332  * Generates a sequential id. This method is typically called when a record is {@link #create}d
33333  * and {@link #Record no id has been specified}. The returned id takes the form:
33334  * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
33335  * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.PREFIX</tt>
33336  * (defaults to <tt>'ext-record'</tt>)</p></li>
33337  * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Record.AUTO_ID</tt>
33338  * (defaults to <tt>1</tt> initially)</p></li>
33339  * </ul></div>
33340  * @param {Record} rec The record being created.  The record does not exist, it's a {@link #phantom}.
33341  * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
33342  */
33343 Ext.data.Record.id = function(rec) {
33344     rec.phantom = true;
33345     return [Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++].join('');
33346 };
33347
33348 Ext.data.Record.prototype = {
33349     /**
33350      * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p>
33351      * A MixedCollection containing the defined {@link Ext.data.Field Field}s for this Record.  Read-only.
33352      * @property fields
33353      * @type Ext.util.MixedCollection
33354      */
33355     /**
33356      * An object hash representing the data for this Record. Every field name in the Record definition
33357      * is represented by a property of that name in this object. Note that unless you specified a field
33358      * with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain
33359      * an <tt>id</tt> property.
33360      * @property data
33361      * @type {Object}
33362      */
33363     /**
33364      * The unique ID of the Record {@link #Record as specified at construction time}.
33365      * @property id
33366      * @type {Object}
33367      */
33368     /**
33369      * <p><b>Only present if this Record was created by an {@link Ext.data.XmlReader XmlReader}</b>.</p>
33370      * <p>The XML element which was the source of the data for this Record.</p>
33371      * @property node
33372      * @type {XMLElement}
33373      */
33374     /**
33375      * <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>
33376      * <p>The Array or object which was the source of the data for this Record.</p>
33377      * @property json
33378      * @type {Array|Object}
33379      */
33380     /**
33381      * Readonly flag - true if this Record has been modified.
33382      * @type Boolean
33383      */
33384     dirty : false,
33385     editing : false,
33386     error : null,
33387     /**
33388      * This object contains a key and value storing the original values of all modified
33389      * fields or is null if no fields have been modified.
33390      * @property modified
33391      * @type {Object}
33392      */
33393     modified : null,
33394     /**
33395      * <tt>true</tt> when the record does not yet exist in a server-side database (see
33396      * {@link #markDirty}).  Any record which has a real database pk set as its id property
33397      * is NOT a phantom -- it's real.
33398      * @property phantom
33399      * @type {Boolean}
33400      */
33401     phantom : false,
33402
33403     // private
33404     join : function(store){
33405         /**
33406          * The {@link Ext.data.Store} to which this Record belongs.
33407          * @property store
33408          * @type {Ext.data.Store}
33409          */
33410         this.store = store;
33411     },
33412
33413     /**
33414      * Set the {@link Ext.data.Field#name named field} to the specified value.  For example:
33415      * <pre><code>
33416 // record has a field named 'firstname'
33417 var Employee = Ext.data.Record.{@link #create}([
33418     {name: 'firstname'},
33419     ...
33420 ]);
33421
33422 // update the 2nd record in the store:
33423 var rec = myStore.{@link Ext.data.Store#getAt getAt}(1);
33424
33425 // set the value (shows dirty flag):
33426 rec.set('firstname', 'Betty');
33427
33428 // commit the change (removes dirty flag):
33429 rec.{@link #commit}();
33430
33431 // update the record in the store, bypass setting dirty flag,
33432 // and do not store the change in the {@link Ext.data.Store#getModifiedRecords modified records}
33433 rec.{@link #data}['firstname'] = 'Wilma'; // updates record, but not the view
33434 rec.{@link #commit}(); // updates the view
33435      * </code></pre>
33436      * <b>Notes</b>:<div class="mdetail-params"><ul>
33437      * <li>If the store has a writer and <code>autoSave=true</code>, each set()
33438      * will execute an XHR to the server.</li>
33439      * <li>Use <code>{@link #beginEdit}</code> to prevent the store's <code>update</code>
33440      * event firing while using set().</li>
33441      * <li>Use <code>{@link #endEdit}</code> to have the store's <code>update</code>
33442      * event fire.</li>
33443      * </ul></div>
33444      * @param {String} name The {@link Ext.data.Field#name name of the field} to set.
33445      * @param {String/Object/Array} value The value to set the field to.
33446      */
33447     set : function(name, value){
33448         var encode = Ext.isPrimitive(value) ? String : Ext.encode;
33449         if(encode(this.data[name]) == encode(value)) {
33450             return;
33451         }        
33452         this.dirty = true;
33453         if(!this.modified){
33454             this.modified = {};
33455         }
33456         if(this.modified[name] === undefined){
33457             this.modified[name] = this.data[name];
33458         }
33459         this.data[name] = value;
33460         if(!this.editing){
33461             this.afterEdit();
33462         }
33463     },
33464
33465     // private
33466     afterEdit : function(){
33467         if (this.store != undefined && typeof this.store.afterEdit == "function") {
33468             this.store.afterEdit(this);
33469         }
33470     },
33471
33472     // private
33473     afterReject : function(){
33474         if(this.store){
33475             this.store.afterReject(this);
33476         }
33477     },
33478
33479     // private
33480     afterCommit : function(){
33481         if(this.store){
33482             this.store.afterCommit(this);
33483         }
33484     },
33485
33486     /**
33487      * Get the value of the {@link Ext.data.Field#name named field}.
33488      * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
33489      * @return {Object} The value of the field.
33490      */
33491     get : function(name){
33492         return this.data[name];
33493     },
33494
33495     /**
33496      * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
33497      * are relayed to the containing store.
33498      * See also: <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
33499      */
33500     beginEdit : function(){
33501         this.editing = true;
33502         this.modified = this.modified || {};
33503     },
33504
33505     /**
33506      * Cancels all changes made in the current edit operation.
33507      */
33508     cancelEdit : function(){
33509         this.editing = false;
33510         delete this.modified;
33511     },
33512
33513     /**
33514      * End an edit. If any data was modified, the containing store is notified
33515      * (ie, the store's <code>update</code> event will fire).
33516      */
33517     endEdit : function(){
33518         this.editing = false;
33519         if(this.dirty){
33520             this.afterEdit();
33521         }
33522     },
33523
33524     /**
33525      * Usually called by the {@link Ext.data.Store} which owns the Record.
33526      * Rejects all changes made to the Record since either creation, or the last commit operation.
33527      * Modified fields are reverted to their original values.
33528      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
33529      * to have their code notified of reject operations.</p>
33530      * @param {Boolean} silent (optional) True to skip notification of the owning
33531      * store of the change (defaults to false)
33532      */
33533     reject : function(silent){
33534         var m = this.modified;
33535         for(var n in m){
33536             if(typeof m[n] != "function"){
33537                 this.data[n] = m[n];
33538             }
33539         }
33540         this.dirty = false;
33541         delete this.modified;
33542         this.editing = false;
33543         if(silent !== true){
33544             this.afterReject();
33545         }
33546     },
33547
33548     /**
33549      * Usually called by the {@link Ext.data.Store} which owns the Record.
33550      * Commits all changes made to the Record since either creation, or the last commit operation.
33551      * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
33552      * to have their code notified of commit operations.</p>
33553      * @param {Boolean} silent (optional) True to skip notification of the owning
33554      * store of the change (defaults to false)
33555      */
33556     commit : function(silent){
33557         this.dirty = false;
33558         delete this.modified;
33559         this.editing = false;
33560         if(silent !== true){
33561             this.afterCommit();
33562         }
33563     },
33564
33565     /**
33566      * Gets a hash of only the fields that have been modified since this Record was created or commited.
33567      * @return Object
33568      */
33569     getChanges : function(){
33570         var m = this.modified, cs = {};
33571         for(var n in m){
33572             if(m.hasOwnProperty(n)){
33573                 cs[n] = this.data[n];
33574             }
33575         }
33576         return cs;
33577     },
33578
33579     // private
33580     hasError : function(){
33581         return this.error !== null;
33582     },
33583
33584     // private
33585     clearError : function(){
33586         this.error = null;
33587     },
33588
33589     /**
33590      * Creates a copy (clone) of this Record.
33591      * @param {String} id (optional) A new Record id, defaults to the id
33592      * of the record being copied. See <code>{@link #id}</code>. 
33593      * To generate a phantom record with a new id use:<pre><code>
33594 var rec = record.copy(); // clone the record
33595 Ext.data.Record.id(rec); // automatically generate a unique sequential id
33596      * </code></pre>
33597      * @return {Record}
33598      */
33599     copy : function(newId) {
33600         return new this.constructor(Ext.apply({}, this.data), newId || this.id);
33601     },
33602
33603     /**
33604      * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
33605      * since the load or last commit.
33606      * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
33607      * @return {Boolean}
33608      */
33609     isModified : function(fieldName){
33610         return !!(this.modified && this.modified.hasOwnProperty(fieldName));
33611     },
33612
33613     /**
33614      * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the
33615      * record configured with <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns
33616      * <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
33617      * @return {Boolean}
33618      */
33619     isValid : function() {
33620         return this.fields.find(function(f) {
33621             return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
33622         },this) ? false : true;
33623     },
33624
33625     /**
33626      * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
33627      * is used interally when adding <code>{@link #phantom}</code> records to a
33628      * {@link Ext.data.Store#writer writer enabled store}.</p>
33629      * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
33630      * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
33631      * have a create action composed for it during {@link Ext.data.Store#save store save}
33632      * operations.</p>
33633      */
33634     markDirty : function(){
33635         this.dirty = true;
33636         if(!this.modified){
33637             this.modified = {};
33638         }
33639         this.fields.each(function(f) {
33640             this.modified[f.name] = this.data[f.name];
33641         },this);
33642     }
33643 };
33644 /**
33645  * @class Ext.StoreMgr
33646  * @extends Ext.util.MixedCollection
33647  * The default global group of stores.
33648  * @singleton
33649  */
33650 Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
33651     /**
33652      * @cfg {Object} listeners @hide
33653      */
33654
33655     /**
33656      * Registers one or more Stores with the StoreMgr. You do not normally need to register stores
33657      * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
33658      * @param {Ext.data.Store} store1 A Store instance
33659      * @param {Ext.data.Store} store2 (optional)
33660      * @param {Ext.data.Store} etc... (optional)
33661      */
33662     register : function(){
33663         for(var i = 0, s; (s = arguments[i]); i++){
33664             this.add(s);
33665         }
33666     },
33667
33668     /**
33669      * Unregisters one or more Stores with the StoreMgr
33670      * @param {String/Object} id1 The id of the Store, or a Store instance
33671      * @param {String/Object} id2 (optional)
33672      * @param {String/Object} etc... (optional)
33673      */
33674     unregister : function(){
33675         for(var i = 0, s; (s = arguments[i]); i++){
33676             this.remove(this.lookup(s));
33677         }
33678     },
33679
33680     /**
33681      * Gets a registered Store by id
33682      * @param {String/Object} id The id of the Store, or a Store instance
33683      * @return {Ext.data.Store}
33684      */
33685     lookup : function(id){
33686         if(Ext.isArray(id)){
33687             var fields = ['field1'], expand = !Ext.isArray(id[0]);
33688             if(!expand){
33689                 for(var i = 2, len = id[0].length; i <= len; ++i){
33690                     fields.push('field' + i);
33691                 }
33692             }
33693             return new Ext.data.ArrayStore({
33694                 fields: fields,
33695                 data: id,
33696                 expandData: expand,
33697                 autoDestroy: true,
33698                 autoCreated: true
33699
33700             });
33701         }
33702         return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
33703     },
33704
33705     // getKey implementation for MixedCollection
33706     getKey : function(o){
33707          return o.storeId;
33708     }
33709 });/**
33710  * @class Ext.data.Store
33711  * @extends Ext.util.Observable
33712  * <p>The Store class encapsulates a client side cache of {@link Ext.data.Record Record}
33713  * objects which provide input data for Components such as the {@link Ext.grid.GridPanel GridPanel},
33714  * the {@link Ext.form.ComboBox ComboBox}, or the {@link Ext.DataView DataView}.</p>
33715  * <p><u>Retrieving Data</u></p>
33716  * <p>A Store object may access a data object using:<div class="mdetail-params"><ul>
33717  * <li>{@link #proxy configured implementation} of {@link Ext.data.DataProxy DataProxy}</li>
33718  * <li>{@link #data} to automatically pass in data</li>
33719  * <li>{@link #loadData} to manually pass in data</li>
33720  * </ul></div></p>
33721  * <p><u>Reading Data</u></p>
33722  * <p>A Store object has no inherent knowledge of the format of the data object (it could be
33723  * an Array, XML, or JSON). A Store object uses an appropriate {@link #reader configured implementation}
33724  * of a {@link Ext.data.DataReader DataReader} to create {@link Ext.data.Record Record} instances from the data
33725  * object.</p>
33726  * <p><u>Store Types</u></p>
33727  * <p>There are several implementations of Store available which are customized for use with
33728  * a specific DataReader implementation.  Here is an example using an ArrayStore which implicitly
33729  * creates a reader commensurate to an Array data object.</p>
33730  * <pre><code>
33731 var myStore = new Ext.data.ArrayStore({
33732     fields: ['fullname', 'first'],
33733     idIndex: 0 // id for each record will be the first element
33734 });
33735  * </code></pre>
33736  * <p>For custom implementations create a basic {@link Ext.data.Store} configured as needed:</p>
33737  * <pre><code>
33738 // create a {@link Ext.data.Record Record} constructor:
33739 var rt = Ext.data.Record.create([
33740     {name: 'fullname'},
33741     {name: 'first'}
33742 ]);
33743 var myStore = new Ext.data.Store({
33744     // explicitly create reader
33745     reader: new Ext.data.ArrayReader(
33746         {
33747             idIndex: 0  // id for each record will be the first element
33748         },
33749         rt // recordType
33750     )
33751 });
33752  * </code></pre>
33753  * <p>Load some data into store (note the data object is an array which corresponds to the reader):</p>
33754  * <pre><code>
33755 var myData = [
33756     [1, 'Fred Flintstone', 'Fred'],  // note that id for the record is the first element
33757     [2, 'Barney Rubble', 'Barney']
33758 ];
33759 myStore.loadData(myData);
33760  * </code></pre>
33761  * <p>Records are cached and made available through accessor functions.  An example of adding
33762  * a record to the store:</p>
33763  * <pre><code>
33764 var defaultData = {
33765     fullname: 'Full Name',
33766     first: 'First Name'
33767 };
33768 var recId = 100; // provide unique id for the record
33769 var r = new myStore.recordType(defaultData, ++recId); // create new record
33770 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
33771  * </code></pre>
33772  * <p><u>Writing Data</u></p>
33773  * <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>
33774  * along with <a href="http://extjs.com/deploy/dev/examples/restful/restful.html">RESTful features.</a>
33775  * @constructor
33776  * Creates a new Store.
33777  * @param {Object} config A config object containing the objects needed for the Store to access data,
33778  * and read the data into Records.
33779  * @xtype store
33780  */
33781 Ext.data.Store = Ext.extend(Ext.util.Observable, {
33782     /**
33783      * @cfg {String} storeId If passed, the id to use to register with the <b>{@link Ext.StoreMgr StoreMgr}</b>.
33784      * <p><b>Note</b>: if a (deprecated) <tt>{@link #id}</tt> is specified it will supersede the <tt>storeId</tt>
33785      * assignment.</p>
33786      */
33787     /**
33788      * @cfg {String} url If a <tt>{@link #proxy}</tt> is not specified the <tt>url</tt> will be used to
33789      * implicitly configure a {@link Ext.data.HttpProxy HttpProxy} if an <tt>url</tt> is specified.
33790      * Typically this option, or the <code>{@link #data}</code> option will be specified.
33791      */
33792     /**
33793      * @cfg {Boolean/Object} autoLoad If <tt>{@link #data}</tt> is not specified, and if <tt>autoLoad</tt>
33794      * is <tt>true</tt> or an <tt>Object</tt>, this store's {@link #load} method is automatically called
33795      * after creation. If the value of <tt>autoLoad</tt> is an <tt>Object</tt>, this <tt>Object</tt> will
33796      * be passed to the store's {@link #load} method.
33797      */
33798     /**
33799      * @cfg {Ext.data.DataProxy} proxy The {@link Ext.data.DataProxy DataProxy} object which provides
33800      * access to a data object.  See <code>{@link #url}</code>.
33801      */
33802     /**
33803      * @cfg {Array} data An inline data object readable by the <code>{@link #reader}</code>.
33804      * Typically this option, or the <code>{@link #url}</code> option will be specified.
33805      */
33806     /**
33807      * @cfg {Ext.data.DataReader} reader The {@link Ext.data.DataReader Reader} object which processes the
33808      * data object and returns an Array of {@link Ext.data.Record} objects which are cached keyed by their
33809      * <b><tt>{@link Ext.data.Record#id id}</tt></b> property.
33810      */
33811     /**
33812      * @cfg {Ext.data.DataWriter} writer
33813      * <p>The {@link Ext.data.DataWriter Writer} object which processes a record object for being written
33814      * to the server-side database.</p>
33815      * <br><p>When a writer is installed into a Store the {@link #add}, {@link #remove}, and {@link #update}
33816      * events on the store are monitored in order to remotely {@link #createRecords create records},
33817      * {@link #destroyRecord destroy records}, or {@link #updateRecord update records}.</p>
33818      * <br><p>The proxy for this store will relay any {@link #writexception} events to this store.</p>
33819      * <br><p>Sample implementation:
33820      * <pre><code>
33821 var writer = new {@link Ext.data.JsonWriter}({
33822     encode: true,
33823     writeAllFields: true // write all fields, not just those that changed
33824 });
33825
33826 // Typical Store collecting the Proxy, Reader and Writer together.
33827 var store = new Ext.data.Store({
33828     storeId: 'user',
33829     root: 'records',
33830     proxy: proxy,
33831     reader: reader,
33832     writer: writer,     // <-- plug a DataWriter into the store just as you would a Reader
33833     paramsAsHash: true,
33834     autoSave: false    // <-- false to delay executing create, update, destroy requests
33835                         //     until specifically told to do so.
33836 });
33837      * </code></pre></p>
33838      */
33839     writer : undefined,
33840     /**
33841      * @cfg {Object} baseParams
33842      * <p>An object containing properties which are to be sent as parameters
33843      * for <i>every</i> HTTP request.</p>
33844      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
33845      * <p><b>Note</b>: <code>baseParams</code> may be superseded by any <code>params</code>
33846      * specified in a <code>{@link #load}</code> request, see <code>{@link #load}</code>
33847      * for more details.</p>
33848      * This property may be modified after creation using the <code>{@link #setBaseParam}</code>
33849      * method.
33850      * @property
33851      */
33852     /**
33853      * @cfg {Object} sortInfo A config object to specify the sort order in the request of a Store's
33854      * {@link #load} operation.  Note that for local sorting, the <tt>direction</tt> property is
33855      * case-sensitive. See also {@link #remoteSort} and {@link #paramNames}.
33856      * For example:<pre><code>
33857 sortInfo: {
33858     field: 'fieldName',
33859     direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
33860 }
33861 </code></pre>
33862      */
33863     /**
33864      * @cfg {boolean} remoteSort <tt>true</tt> if sorting is to be handled by requesting the <tt>{@link #proxy Proxy}</tt>
33865      * to provide a refreshed version of the data object in sorted order, as opposed to sorting the Record cache
33866      * in place (defaults to <tt>false</tt>).
33867      * <p>If <tt>remoteSort</tt> is <tt>true</tt>, then clicking on a {@link Ext.grid.Column Grid Column}'s
33868      * {@link Ext.grid.Column#header header} causes the current page to be requested from the server appending
33869      * the following two parameters to the <b><tt>{@link #load params}</tt></b>:<div class="mdetail-params"><ul>
33870      * <li><b><tt>sort</tt></b> : String<p class="sub-desc">The <tt>name</tt> (as specified in the Record's
33871      * {@link Ext.data.Field Field definition}) of the field to sort on.</p></li>
33872      * <li><b><tt>dir</tt></b> : String<p class="sub-desc">The direction of the sort, 'ASC' or 'DESC' (case-sensitive).</p></li>
33873      * </ul></div></p>
33874      */
33875     remoteSort : false,
33876
33877     /**
33878      * @cfg {Boolean} autoDestroy <tt>true</tt> to destroy the store when the component the store is bound
33879      * to is destroyed (defaults to <tt>false</tt>).
33880      * <p><b>Note</b>: this should be set to true when using stores that are bound to only 1 component.</p>
33881      */
33882     autoDestroy : false,
33883
33884     /**
33885      * @cfg {Boolean} pruneModifiedRecords <tt>true</tt> to clear all modified record information each time
33886      * the store is loaded or when a record is removed (defaults to <tt>false</tt>). See {@link #getModifiedRecords}
33887      * for the accessor method to retrieve the modified records.
33888      */
33889     pruneModifiedRecords : false,
33890
33891     /**
33892      * Contains the last options object used as the parameter to the {@link #load} method. See {@link #load}
33893      * for the details of what this may contain. This may be useful for accessing any params which were used
33894      * to load the current Record cache.
33895      * @property
33896      */
33897     lastOptions : null,
33898
33899     /**
33900      * @cfg {Boolean} autoSave
33901      * <p>Defaults to <tt>true</tt> causing the store to automatically {@link #save} records to
33902      * the server when a record is modified (ie: becomes 'dirty'). Specify <tt>false</tt> to manually call {@link #save}
33903      * to send all modifiedRecords to the server.</p>
33904      * <br><p><b>Note</b>: each CRUD action will be sent as a separate request.</p>
33905      */
33906     autoSave : true,
33907
33908     /**
33909      * @cfg {Boolean} batch
33910      * <p>Defaults to <tt>true</tt> (unless <code>{@link #restful}:true</code>). Multiple
33911      * requests for each CRUD action (CREATE, READ, UPDATE and DESTROY) will be combined
33912      * and sent as one transaction. Only applies when <code>{@link #autoSave}</code> is set
33913      * to <tt>false</tt>.</p>
33914      * <br><p>If Store is RESTful, the DataProxy is also RESTful, and a unique transaction is
33915      * generated for each record.</p>
33916      */
33917     batch : true,
33918
33919     /**
33920      * @cfg {Boolean} restful
33921      * Defaults to <tt>false</tt>.  Set to <tt>true</tt> to have the Store and the set
33922      * Proxy operate in a RESTful manner. The store will automatically generate GET, POST,
33923      * PUT and DELETE requests to the server. The HTTP method used for any given CRUD
33924      * action is described in {@link Ext.data.Api#restActions}.  For additional information
33925      * see {@link Ext.data.DataProxy#restful}.
33926      * <p><b>Note</b>: if <code>{@link #restful}:true</code> <code>batch</code> will
33927      * internally be set to <tt>false</tt>.</p>
33928      */
33929     restful: false,
33930
33931     /**
33932      * @cfg {Object} paramNames
33933      * <p>An object containing properties which specify the names of the paging and
33934      * sorting parameters passed to remote servers when loading blocks of data. By default, this
33935      * object takes the following form:</p><pre><code>
33936 {
33937     start : 'start',  // The parameter name which specifies the start row
33938     limit : 'limit',  // The parameter name which specifies number of rows to return
33939     sort : 'sort',    // The parameter name which specifies the column to sort on
33940     dir : 'dir'       // The parameter name which specifies the sort direction
33941 }
33942 </code></pre>
33943      * <p>The server must produce the requested data block upon receipt of these parameter names.
33944      * If different parameter names are required, this property can be overriden using a configuration
33945      * property.</p>
33946      * <p>A {@link Ext.PagingToolbar PagingToolbar} bound to this Store uses this property to determine
33947      * the parameter names to use in its {@link #load requests}.
33948      */
33949     paramNames : undefined,
33950
33951     /**
33952      * @cfg {Object} defaultParamNames
33953      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
33954      * for all stores, this object should be changed on the store prototype.
33955      */
33956     defaultParamNames : {
33957         start : 'start',
33958         limit : 'limit',
33959         sort : 'sort',
33960         dir : 'dir'
33961     },
33962
33963     /**
33964      * @property isDestroyed
33965      * @type Boolean
33966      * True if the store has been destroyed already. Read only
33967      */
33968     isDestroyed: false,
33969
33970     /**
33971      * @property hasMultiSort
33972      * @type Boolean
33973      * True if this store is currently sorted by more than one field/direction combination.
33974      */
33975     hasMultiSort: false,
33976
33977     // private
33978     batchKey : '_ext_batch_',
33979
33980     constructor : function(config){
33981         this.data = new Ext.util.MixedCollection(false);
33982         this.data.getKey = function(o){
33983             return o.id;
33984         };
33985
33986
33987         // temporary removed-records cache
33988         this.removed = [];
33989
33990         if(config && config.data){
33991             this.inlineData = config.data;
33992             delete config.data;
33993         }
33994
33995         Ext.apply(this, config);
33996
33997         /**
33998          * See the <code>{@link #baseParams corresponding configuration option}</code>
33999          * for a description of this property.
34000          * To modify this property see <code>{@link #setBaseParam}</code>.
34001          * @property
34002          */
34003         this.baseParams = Ext.isObject(this.baseParams) ? this.baseParams : {};
34004
34005         this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
34006
34007         if((this.url || this.api) && !this.proxy){
34008             this.proxy = new Ext.data.HttpProxy({url: this.url, api: this.api});
34009         }
34010         // If Store is RESTful, so too is the DataProxy
34011         if (this.restful === true && this.proxy) {
34012             // When operating RESTfully, a unique transaction is generated for each record.
34013             // TODO might want to allow implemention of faux REST where batch is possible using RESTful routes only.
34014             this.batch = false;
34015             Ext.data.Api.restify(this.proxy);
34016         }
34017
34018         if(this.reader){ // reader passed
34019             if(!this.recordType){
34020                 this.recordType = this.reader.recordType;
34021             }
34022             if(this.reader.onMetaChange){
34023                 this.reader.onMetaChange = this.reader.onMetaChange.createSequence(this.onMetaChange, this);
34024             }
34025             if (this.writer) { // writer passed
34026                 if (this.writer instanceof(Ext.data.DataWriter) === false) {    // <-- config-object instead of instance.
34027                     this.writer = this.buildWriter(this.writer);
34028                 }
34029                 this.writer.meta = this.reader.meta;
34030                 this.pruneModifiedRecords = true;
34031             }
34032         }
34033
34034         /**
34035          * The {@link Ext.data.Record Record} constructor as supplied to (or created by) the
34036          * {@link Ext.data.DataReader Reader}. Read-only.
34037          * <p>If the Reader was constructed by passing in an Array of {@link Ext.data.Field} definition objects,
34038          * instead of a Record constructor, it will implicitly create a Record constructor from that Array (see
34039          * {@link Ext.data.Record}.{@link Ext.data.Record#create create} for additional details).</p>
34040          * <p>This property may be used to create new Records of the type held in this Store, for example:</p><pre><code>
34041     // create the data store
34042     var store = new Ext.data.ArrayStore({
34043         autoDestroy: true,
34044         fields: [
34045            {name: 'company'},
34046            {name: 'price', type: 'float'},
34047            {name: 'change', type: 'float'},
34048            {name: 'pctChange', type: 'float'},
34049            {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
34050         ]
34051     });
34052     store.loadData(myData);
34053
34054     // create the Grid
34055     var grid = new Ext.grid.EditorGridPanel({
34056         store: store,
34057         colModel: new Ext.grid.ColumnModel({
34058             columns: [
34059                 {id:'company', header: 'Company', width: 160, dataIndex: 'company'},
34060                 {header: 'Price', renderer: 'usMoney', dataIndex: 'price'},
34061                 {header: 'Change', renderer: change, dataIndex: 'change'},
34062                 {header: '% Change', renderer: pctChange, dataIndex: 'pctChange'},
34063                 {header: 'Last Updated', width: 85,
34064                     renderer: Ext.util.Format.dateRenderer('m/d/Y'),
34065                     dataIndex: 'lastChange'}
34066             ],
34067             defaults: {
34068                 sortable: true,
34069                 width: 75
34070             }
34071         }),
34072         autoExpandColumn: 'company', // match the id specified in the column model
34073         height:350,
34074         width:600,
34075         title:'Array Grid',
34076         tbar: [{
34077             text: 'Add Record',
34078             handler : function(){
34079                 var defaultData = {
34080                     change: 0,
34081                     company: 'New Company',
34082                     lastChange: (new Date()).clearTime(),
34083                     pctChange: 0,
34084                     price: 10
34085                 };
34086                 var recId = 3; // provide unique id
34087                 var p = new store.recordType(defaultData, recId); // create new record
34088                 grid.stopEditing();
34089                 store.{@link #insert}(0, p); // insert a new record into the store (also see {@link #add})
34090                 grid.startEditing(0, 0);
34091             }
34092         }]
34093     });
34094          * </code></pre>
34095          * @property recordType
34096          * @type Function
34097          */
34098
34099         if(this.recordType){
34100             /**
34101              * A {@link Ext.util.MixedCollection MixedCollection} containing the defined {@link Ext.data.Field Field}s
34102              * for the {@link Ext.data.Record Records} stored in this Store. Read-only.
34103              * @property fields
34104              * @type Ext.util.MixedCollection
34105              */
34106             this.fields = this.recordType.prototype.fields;
34107         }
34108         this.modified = [];
34109
34110         this.addEvents(
34111             /**
34112              * @event datachanged
34113              * Fires when the data cache has changed in a bulk manner (e.g., it has been sorted, filtered, etc.) and a
34114              * widget that is using this Store as a Record cache should refresh its view.
34115              * @param {Store} this
34116              */
34117             'datachanged',
34118             /**
34119              * @event metachange
34120              * Fires when this store's reader provides new metadata (fields). This is currently only supported for JsonReaders.
34121              * @param {Store} this
34122              * @param {Object} meta The JSON metadata
34123              */
34124             'metachange',
34125             /**
34126              * @event add
34127              * Fires when Records have been {@link #add}ed to the Store
34128              * @param {Store} this
34129              * @param {Ext.data.Record[]} records The array of Records added
34130              * @param {Number} index The index at which the record(s) were added
34131              */
34132             'add',
34133             /**
34134              * @event remove
34135              * Fires when a Record has been {@link #remove}d from the Store
34136              * @param {Store} this
34137              * @param {Ext.data.Record} record The Record that was removed
34138              * @param {Number} index The index at which the record was removed
34139              */
34140             'remove',
34141             /**
34142              * @event update
34143              * Fires when a Record has been updated
34144              * @param {Store} this
34145              * @param {Ext.data.Record} record The Record that was updated
34146              * @param {String} operation The update operation being performed.  Value may be one of:
34147              * <pre><code>
34148      Ext.data.Record.EDIT
34149      Ext.data.Record.REJECT
34150      Ext.data.Record.COMMIT
34151              * </code></pre>
34152              */
34153             'update',
34154             /**
34155              * @event clear
34156              * Fires when the data cache has been cleared.
34157              * @param {Store} this
34158              * @param {Record[]} The records that were cleared.
34159              */
34160             'clear',
34161             /**
34162              * @event exception
34163              * <p>Fires if an exception occurs in the Proxy during a remote request.
34164              * This event is relayed through the corresponding {@link Ext.data.DataProxy}.
34165              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
34166              * for additional details.
34167              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
34168              * for description.
34169              */
34170             'exception',
34171             /**
34172              * @event beforeload
34173              * Fires before a request is made for a new data object.  If the beforeload handler returns
34174              * <tt>false</tt> the {@link #load} action will be canceled.
34175              * @param {Store} this
34176              * @param {Object} options The loading options that were specified (see {@link #load} for details)
34177              */
34178             'beforeload',
34179             /**
34180              * @event load
34181              * Fires after a new set of Records has been loaded.
34182              * @param {Store} this
34183              * @param {Ext.data.Record[]} records The Records that were loaded
34184              * @param {Object} options The loading options that were specified (see {@link #load} for details)
34185              */
34186             'load',
34187             /**
34188              * @event loadexception
34189              * <p>This event is <b>deprecated</b> in favor of the catch-all <b><code>{@link #exception}</code></b>
34190              * event instead.</p>
34191              * <p>This event is relayed through the corresponding {@link Ext.data.DataProxy}.
34192              * See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
34193              * for additional details.
34194              * @param {misc} misc See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#loadexception loadexception}
34195              * for description.
34196              */
34197             'loadexception',
34198             /**
34199              * @event beforewrite
34200              * @param {Ext.data.Store} store
34201              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
34202              * @param {Record/Record[]} rs The Record(s) being written.
34203              * @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)
34204              * @param {Object} arg The callback's arg object passed to the {@link #request} function
34205              */
34206             'beforewrite',
34207             /**
34208              * @event write
34209              * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
34210              * Success of the action is determined in the <code>result['successProperty']</code>property (<b>NOTE</b> for RESTful stores,
34211              * a simple 20x response is sufficient for the actions "destroy" and "update".  The "create" action should should return 200 along with a database pk).
34212              * @param {Ext.data.Store} store
34213              * @param {String} action [Ext.data.Api.actions.create|update|destroy]
34214              * @param {Object} result The 'data' picked-out out of the response for convenience.
34215              * @param {Ext.Direct.Transaction} res
34216              * @param {Record/Record[]} rs Store's records, the subject(s) of the write-action
34217              */
34218             'write',
34219             /**
34220              * @event beforesave
34221              * Fires before a save action is called. A save encompasses destroying records, updating records and creating records.
34222              * @param {Ext.data.Store} store
34223              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
34224              * with an array of records for each action.
34225              */
34226             'beforesave',
34227             /**
34228              * @event save
34229              * Fires after a save is completed. A save encompasses destroying records, updating records and creating records.
34230              * @param {Ext.data.Store} store
34231              * @param {Number} batch The identifier for the batch that was saved.
34232              * @param {Object} data An object containing the data that is to be saved. The object will contain a key for each appropriate action,
34233              * with an array of records for each action.
34234              */
34235             'save'
34236
34237         );
34238
34239         if(this.proxy){
34240             // TODO remove deprecated loadexception with ext-3.0.1
34241             this.relayEvents(this.proxy,  ['loadexception', 'exception']);
34242         }
34243         // With a writer set for the Store, we want to listen to add/remove events to remotely create/destroy records.
34244         if (this.writer) {
34245             this.on({
34246                 scope: this,
34247                 add: this.createRecords,
34248                 remove: this.destroyRecord,
34249                 update: this.updateRecord,
34250                 clear: this.onClear
34251             });
34252         }
34253
34254         this.sortToggle = {};
34255         if(this.sortField){
34256             this.setDefaultSort(this.sortField, this.sortDir);
34257         }else if(this.sortInfo){
34258             this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
34259         }
34260
34261         Ext.data.Store.superclass.constructor.call(this);
34262
34263         if(this.id){
34264             this.storeId = this.id;
34265             delete this.id;
34266         }
34267         if(this.storeId){
34268             Ext.StoreMgr.register(this);
34269         }
34270         if(this.inlineData){
34271             this.loadData(this.inlineData);
34272             delete this.inlineData;
34273         }else if(this.autoLoad){
34274             this.load.defer(10, this, [
34275                 typeof this.autoLoad == 'object' ?
34276                     this.autoLoad : undefined]);
34277         }
34278         // used internally to uniquely identify a batch
34279         this.batchCounter = 0;
34280         this.batches = {};
34281     },
34282
34283     /**
34284      * builds a DataWriter instance when Store constructor is provided with a writer config-object instead of an instace.
34285      * @param {Object} config Writer configuration
34286      * @return {Ext.data.DataWriter}
34287      * @private
34288      */
34289     buildWriter : function(config) {
34290         var klass = undefined,
34291             type = (config.format || 'json').toLowerCase();
34292         switch (type) {
34293             case 'json':
34294                 klass = Ext.data.JsonWriter;
34295                 break;
34296             case 'xml':
34297                 klass = Ext.data.XmlWriter;
34298                 break;
34299             default:
34300                 klass = Ext.data.JsonWriter;
34301         }
34302         return new klass(config);
34303     },
34304
34305     /**
34306      * Destroys the store.
34307      */
34308     destroy : function(){
34309         if(!this.isDestroyed){
34310             if(this.storeId){
34311                 Ext.StoreMgr.unregister(this);
34312             }
34313             this.clearData();
34314             this.data = null;
34315             Ext.destroy(this.proxy);
34316             this.reader = this.writer = null;
34317             this.purgeListeners();
34318             this.isDestroyed = true;
34319         }
34320     },
34321
34322     /**
34323      * Add Records to the Store and fires the {@link #add} event.  To add Records
34324      * to the store from a remote source use <code>{@link #load}({add:true})</code>.
34325      * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
34326      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
34327      * to add to the cache. See {@link #recordType}.
34328      */
34329     add : function(records){
34330         records = [].concat(records);
34331         if(records.length < 1){
34332             return;
34333         }
34334         for(var i = 0, len = records.length; i < len; i++){
34335             records[i].join(this);
34336         }
34337         var index = this.data.length;
34338         this.data.addAll(records);
34339         if(this.snapshot){
34340             this.snapshot.addAll(records);
34341         }
34342         this.fireEvent('add', this, records, index);
34343     },
34344
34345     /**
34346      * (Local sort only) Inserts the passed Record into the Store at the index where it
34347      * should go based on the current sort information.
34348      * @param {Ext.data.Record} record
34349      */
34350     addSorted : function(record){
34351         var index = this.findInsertIndex(record);
34352         this.insert(index, record);
34353     },
34354
34355     /**
34356      * Remove Records from the Store and fires the {@link #remove} event.
34357      * @param {Ext.data.Record/Ext.data.Record[]} record The record object or array of records to remove from the cache.
34358      */
34359     remove : function(record){
34360         if(Ext.isArray(record)){
34361             Ext.each(record, function(r){
34362                 this.remove(r);
34363             }, this);
34364             return;
34365         }
34366         var index = this.data.indexOf(record);
34367         if(index > -1){
34368             record.join(null);
34369             this.data.removeAt(index);
34370         }
34371         if(this.pruneModifiedRecords){
34372             this.modified.remove(record);
34373         }
34374         if(this.snapshot){
34375             this.snapshot.remove(record);
34376         }
34377         if(index > -1){
34378             this.fireEvent('remove', this, record, index);
34379         }
34380     },
34381
34382     /**
34383      * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
34384      * @param {Number} index The index of the record to remove.
34385      */
34386     removeAt : function(index){
34387         this.remove(this.getAt(index));
34388     },
34389
34390     /**
34391      * Remove all Records from the Store and fires the {@link #clear} event.
34392      * @param {Boolean} silent [false] Defaults to <tt>false</tt>.  Set <tt>true</tt> to not fire clear event.
34393      */
34394     removeAll : function(silent){
34395         var items = [];
34396         this.each(function(rec){
34397             items.push(rec);
34398         });
34399         this.clearData();
34400         if(this.snapshot){
34401             this.snapshot.clear();
34402         }
34403         if(this.pruneModifiedRecords){
34404             this.modified = [];
34405         }
34406         if (silent !== true) {  // <-- prevents write-actions when we just want to clear a store.
34407             this.fireEvent('clear', this, items);
34408         }
34409     },
34410
34411     // private
34412     onClear: function(store, records){
34413         Ext.each(records, function(rec, index){
34414             this.destroyRecord(this, rec, index);
34415         }, this);
34416     },
34417
34418     /**
34419      * Inserts Records into the Store at the given index and fires the {@link #add} event.
34420      * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
34421      * @param {Number} index The start index at which to insert the passed Records.
34422      * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
34423      */
34424     insert : function(index, records){
34425         records = [].concat(records);
34426         for(var i = 0, len = records.length; i < len; i++){
34427             this.data.insert(index, records[i]);
34428             records[i].join(this);
34429         }
34430         if(this.snapshot){
34431             this.snapshot.addAll(records);
34432         }
34433         this.fireEvent('add', this, records, index);
34434     },
34435
34436     /**
34437      * Get the index within the cache of the passed Record.
34438      * @param {Ext.data.Record} record The Ext.data.Record object to find.
34439      * @return {Number} The index of the passed Record. Returns -1 if not found.
34440      */
34441     indexOf : function(record){
34442         return this.data.indexOf(record);
34443     },
34444
34445     /**
34446      * Get the index within the cache of the Record with the passed id.
34447      * @param {String} id The id of the Record to find.
34448      * @return {Number} The index of the Record. Returns -1 if not found.
34449      */
34450     indexOfId : function(id){
34451         return this.data.indexOfKey(id);
34452     },
34453
34454     /**
34455      * Get the Record with the specified id.
34456      * @param {String} id The id of the Record to find.
34457      * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
34458      */
34459     getById : function(id){
34460         return (this.snapshot || this.data).key(id);
34461     },
34462
34463     /**
34464      * Get the Record at the specified index.
34465      * @param {Number} index The index of the Record to find.
34466      * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
34467      */
34468     getAt : function(index){
34469         return this.data.itemAt(index);
34470     },
34471
34472     /**
34473      * Returns a range of Records between specified indices.
34474      * @param {Number} startIndex (optional) The starting index (defaults to 0)
34475      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
34476      * @return {Ext.data.Record[]} An array of Records
34477      */
34478     getRange : function(start, end){
34479         return this.data.getRange(start, end);
34480     },
34481
34482     // private
34483     storeOptions : function(o){
34484         o = Ext.apply({}, o);
34485         delete o.callback;
34486         delete o.scope;
34487         this.lastOptions = o;
34488     },
34489
34490     // private
34491     clearData: function(){
34492         this.data.each(function(rec) {
34493             rec.join(null);
34494         });
34495         this.data.clear();
34496     },
34497
34498     /**
34499      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
34500      * <br><p>Notes:</p><div class="mdetail-params"><ul>
34501      * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
34502      * loaded. To perform any post-processing where information from the load call is required, specify
34503      * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
34504      * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
34505      * properties in the <code>options.params</code> property to establish the initial position within the
34506      * dataset, and the number of Records to cache on each read from the Proxy.</li>
34507      * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
34508      * will be automatically included with the posted parameters according to the specified
34509      * <code>{@link #paramNames}</code>.</li>
34510      * </ul></div>
34511      * @param {Object} options An object containing properties which control loading options:<ul>
34512      * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
34513      * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
34514      * <code>{@link #baseParams}</code> of the same name.</p>
34515      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
34516      * <li><b>callback</b> : Function<div class="sub-desc"><p>A function to be called after the Records
34517      * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
34518      * <li>r : Ext.data.Record[] An Array of Records loaded.</li>
34519      * <li>options : Options object from the load call.</li>
34520      * <li>success : Boolean success indicator.</li></ul></p></div></li>
34521      * <li><b>scope</b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
34522      * to the Store object)</p></div></li>
34523      * <li><b>add</b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
34524      * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
34525      * </ul>
34526      * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
34527      * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
34528      */
34529     load : function(options) {
34530         options = Ext.apply({}, options);
34531         this.storeOptions(options);
34532         if(this.sortInfo && this.remoteSort){
34533             var pn = this.paramNames;
34534             options.params = Ext.apply({}, options.params);
34535             options.params[pn.sort] = this.sortInfo.field;
34536             options.params[pn.dir] = this.sortInfo.direction;
34537         }
34538         try {
34539             return this.execute('read', null, options); // <-- null represents rs.  No rs for load actions.
34540         } catch(e) {
34541             this.handleException(e);
34542             return false;
34543         }
34544     },
34545
34546     /**
34547      * updateRecord  Should not be used directly.  This method will be called automatically if a Writer is set.
34548      * Listens to 'update' event.
34549      * @param {Object} store
34550      * @param {Object} record
34551      * @param {Object} action
34552      * @private
34553      */
34554     updateRecord : function(store, record, action) {
34555         if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid()))) {
34556             this.save();
34557         }
34558     },
34559
34560     /**
34561      * Should not be used directly.  Store#add will call this automatically if a Writer is set
34562      * @param {Object} store
34563      * @param {Object} rs
34564      * @param {Object} index
34565      * @private
34566      */
34567     createRecords : function(store, rs, index) {
34568         for (var i = 0, len = rs.length; i < len; i++) {
34569             if (rs[i].phantom && rs[i].isValid()) {
34570                 rs[i].markDirty();  // <-- Mark new records dirty
34571                 this.modified.push(rs[i]);  // <-- add to modified
34572             }
34573         }
34574         if (this.autoSave === true) {
34575             this.save();
34576         }
34577     },
34578
34579     /**
34580      * Destroys a Record.  Should not be used directly.  It's called by Store#remove if a Writer is set.
34581      * @param {Store} store this
34582      * @param {Ext.data.Record} record
34583      * @param {Number} index
34584      * @private
34585      */
34586     destroyRecord : function(store, record, index) {
34587         if (this.modified.indexOf(record) != -1) {  // <-- handled already if @cfg pruneModifiedRecords == true
34588             this.modified.remove(record);
34589         }
34590         if (!record.phantom) {
34591             this.removed.push(record);
34592
34593             // since the record has already been removed from the store but the server request has not yet been executed,
34594             // must keep track of the last known index this record existed.  If a server error occurs, the record can be
34595             // put back into the store.  @see Store#createCallback where the record is returned when response status === false
34596             record.lastIndex = index;
34597
34598             if (this.autoSave === true) {
34599                 this.save();
34600             }
34601         }
34602     },
34603
34604     /**
34605      * This method should generally not be used directly.  This method is called internally
34606      * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
34607      * {@link #remove}, or {@link #update} events fire.
34608      * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
34609      * @param {Record/Record[]} rs
34610      * @param {Object} options
34611      * @throws Error
34612      * @private
34613      */
34614     execute : function(action, rs, options, /* private */ batch) {
34615         // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
34616         if (!Ext.data.Api.isAction(action)) {
34617             throw new Ext.data.Api.Error('execute', action);
34618         }
34619         // make sure options has a fresh, new params hash
34620         options = Ext.applyIf(options||{}, {
34621             params: {}
34622         });
34623         if(batch !== undefined){
34624             this.addToBatch(batch);
34625         }
34626         // have to separate before-events since load has a different signature than create,destroy and save events since load does not
34627         // include the rs (record resultset) parameter.  Capture return values from the beforeaction into doRequest flag.
34628         var doRequest = true;
34629
34630         if (action === 'read') {
34631             doRequest = this.fireEvent('beforeload', this, options);
34632             Ext.applyIf(options.params, this.baseParams);
34633         }
34634         else {
34635             // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
34636             // TODO Move listful rendering into DataWriter where the @cfg is defined.  Should be easy now.
34637             if (this.writer.listful === true && this.restful !== true) {
34638                 rs = (Ext.isArray(rs)) ? rs : [rs];
34639             }
34640             // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
34641             else if (Ext.isArray(rs) && rs.length == 1) {
34642                 rs = rs.shift();
34643             }
34644             // Write the action to options.params
34645             if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
34646                 this.writer.apply(options.params, this.baseParams, action, rs);
34647             }
34648         }
34649         if (doRequest !== false) {
34650             // Send request to proxy.
34651             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
34652                 options.params.xaction = action;    // <-- really old, probaby unecessary.
34653             }
34654             // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
34655             // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
34656             // the user's configured DataProxy#api
34657             // TODO Refactor all Proxies to accept an instance of Ext.data.Request (not yet defined) instead of this looooooong list
34658             // of params.  This method is an artifact from Ext2.
34659             this.proxy.request(Ext.data.Api.actions[action], rs, options.params, this.reader, this.createCallback(action, rs, batch), this, options);
34660         }
34661         return doRequest;
34662     },
34663
34664     /**
34665      * Saves all pending changes to the store.  If the commensurate Ext.data.Api.actions action is not configured, then
34666      * the configured <code>{@link #url}</code> will be used.
34667      * <pre>
34668      * change            url
34669      * ---------------   --------------------
34670      * removed records   Ext.data.Api.actions.destroy
34671      * phantom records   Ext.data.Api.actions.create
34672      * {@link #getModifiedRecords modified records}  Ext.data.Api.actions.update
34673      * </pre>
34674      * @TODO:  Create extensions of Error class and send associated Record with thrown exceptions.
34675      * e.g.:  Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
34676      * @return {Number} batch Returns a number to uniquely identify the "batch" of saves occurring. -1 will be returned
34677      * if there are no items to save or the save was cancelled.
34678      */
34679     save : function() {
34680         if (!this.writer) {
34681             throw new Ext.data.Store.Error('writer-undefined');
34682         }
34683
34684         var queue = [],
34685             len,
34686             trans,
34687             batch,
34688             data = {};
34689         // DESTROY:  First check for removed records.  Records in this.removed are guaranteed non-phantoms.  @see Store#remove
34690         if(this.removed.length){
34691             queue.push(['destroy', this.removed]);
34692         }
34693
34694         // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
34695         var rs = [].concat(this.getModifiedRecords());
34696         if(rs.length){
34697             // CREATE:  Next check for phantoms within rs.  splice-off and execute create.
34698             var phantoms = [];
34699             for(var i = rs.length-1; i >= 0; i--){
34700                 if(rs[i].phantom === true){
34701                     var rec = rs.splice(i, 1).shift();
34702                     if(rec.isValid()){
34703                         phantoms.push(rec);
34704                     }
34705                 }else if(!rs[i].isValid()){ // <-- while we're here, splice-off any !isValid real records
34706                     rs.splice(i,1);
34707                 }
34708             }
34709             // If we have valid phantoms, create them...
34710             if(phantoms.length){
34711                 queue.push(['create', phantoms]);
34712             }
34713
34714             // UPDATE:  And finally, if we're still here after splicing-off phantoms and !isValid real records, update the rest...
34715             if(rs.length){
34716                 queue.push(['update', rs]);
34717             }
34718         }
34719         len = queue.length;
34720         if(len){
34721             batch = ++this.batchCounter;
34722             for(var i = 0; i < len; ++i){
34723                 trans = queue[i];
34724                 data[trans[0]] = trans[1];
34725             }
34726             if(this.fireEvent('beforesave', this, data) !== false){
34727                 for(var i = 0; i < len; ++i){
34728                     trans = queue[i];
34729                     this.doTransaction(trans[0], trans[1], batch);
34730                 }
34731                 return batch;
34732             }
34733         }
34734         return -1;
34735     },
34736
34737     // private.  Simply wraps call to Store#execute in try/catch.  Defers to Store#handleException on error.  Loops if batch: false
34738     doTransaction : function(action, rs, batch) {
34739         function transaction(records) {
34740             try{
34741                 this.execute(action, records, undefined, batch);
34742             }catch (e){
34743                 this.handleException(e);
34744             }
34745         }
34746         if(this.batch === false){
34747             for(var i = 0, len = rs.length; i < len; i++){
34748                 transaction.call(this, rs[i]);
34749             }
34750         }else{
34751             transaction.call(this, rs);
34752         }
34753     },
34754
34755     // private
34756     addToBatch : function(batch){
34757         var b = this.batches,
34758             key = this.batchKey + batch,
34759             o = b[key];
34760
34761         if(!o){
34762             b[key] = o = {
34763                 id: batch,
34764                 count: 0,
34765                 data: {}
34766             };
34767         }
34768         ++o.count;
34769     },
34770
34771     removeFromBatch : function(batch, action, data){
34772         var b = this.batches,
34773             key = this.batchKey + batch,
34774             o = b[key],
34775             data,
34776             arr;
34777
34778
34779         if(o){
34780             arr = o.data[action] || [];
34781             o.data[action] = arr.concat(data);
34782             if(o.count === 1){
34783                 data = o.data;
34784                 delete b[key];
34785                 this.fireEvent('save', this, batch, data);
34786             }else{
34787                 --o.count;
34788             }
34789         }
34790     },
34791
34792     // @private callback-handler for remote CRUD actions
34793     // Do not override -- override loadRecords, onCreateRecords, onDestroyRecords and onUpdateRecords instead.
34794     createCallback : function(action, rs, batch) {
34795         var actions = Ext.data.Api.actions;
34796         return (action == 'read') ? this.loadRecords : function(data, response, success) {
34797             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
34798             this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, [].concat(data));
34799             // If success === false here, exception will have been called in DataProxy
34800             if (success === true) {
34801                 this.fireEvent('write', this, action, data, response, rs);
34802             }
34803             this.removeFromBatch(batch, action, data);
34804         };
34805     },
34806
34807     // Clears records from modified array after an exception event.
34808     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
34809     // TODO remove this method?
34810     clearModified : function(rs) {
34811         if (Ext.isArray(rs)) {
34812             for (var n=rs.length-1;n>=0;n--) {
34813                 this.modified.splice(this.modified.indexOf(rs[n]), 1);
34814             }
34815         } else {
34816             this.modified.splice(this.modified.indexOf(rs), 1);
34817         }
34818     },
34819
34820     // remap record ids in MixedCollection after records have been realized.  @see Store#onCreateRecords, @see DataReader#realize
34821     reMap : function(record) {
34822         if (Ext.isArray(record)) {
34823             for (var i = 0, len = record.length; i < len; i++) {
34824                 this.reMap(record[i]);
34825             }
34826         } else {
34827             delete this.data.map[record._phid];
34828             this.data.map[record.id] = record;
34829             var index = this.data.keys.indexOf(record._phid);
34830             this.data.keys.splice(index, 1, record.id);
34831             delete record._phid;
34832         }
34833     },
34834
34835     // @protected onCreateRecord proxy callback for create action
34836     onCreateRecords : function(success, rs, data) {
34837         if (success === true) {
34838             try {
34839                 this.reader.realize(rs, data);
34840                 this.reMap(rs);
34841             }
34842             catch (e) {
34843                 this.handleException(e);
34844                 if (Ext.isArray(rs)) {
34845                     // Recurse to run back into the try {}.  DataReader#realize splices-off the rs until empty.
34846                     this.onCreateRecords(success, rs, data);
34847                 }
34848             }
34849         }
34850     },
34851
34852     // @protected, onUpdateRecords proxy callback for update action
34853     onUpdateRecords : function(success, rs, data) {
34854         if (success === true) {
34855             try {
34856                 this.reader.update(rs, data);
34857             } catch (e) {
34858                 this.handleException(e);
34859                 if (Ext.isArray(rs)) {
34860                     // Recurse to run back into the try {}.  DataReader#update splices-off the rs until empty.
34861                     this.onUpdateRecords(success, rs, data);
34862                 }
34863             }
34864         }
34865     },
34866
34867     // @protected onDestroyRecords proxy callback for destroy action
34868     onDestroyRecords : function(success, rs, data) {
34869         // splice each rec out of this.removed
34870         rs = (rs instanceof Ext.data.Record) ? [rs] : [].concat(rs);
34871         for (var i=0,len=rs.length;i<len;i++) {
34872             this.removed.splice(this.removed.indexOf(rs[i]), 1);
34873         }
34874         if (success === false) {
34875             // put records back into store if remote destroy fails.
34876             // @TODO: Might want to let developer decide.
34877             for (i=rs.length-1;i>=0;i--) {
34878                 this.insert(rs[i].lastIndex, rs[i]);    // <-- lastIndex set in Store#destroyRecord
34879             }
34880         }
34881     },
34882
34883     // protected handleException.  Possibly temporary until Ext framework has an exception-handler.
34884     handleException : function(e) {
34885         // @see core/Error.js
34886         Ext.handleError(e);
34887     },
34888
34889     /**
34890      * <p>Reloads the Record cache from the configured Proxy using the configured
34891      * {@link Ext.data.Reader Reader} and the options from the last load operation
34892      * performed.</p>
34893      * <p><b>Note</b>: see the Important note in {@link #load}.</p>
34894      * @param {Object} options <p>(optional) An <tt>Object</tt> containing
34895      * {@link #load loading options} which may override the {@link #lastOptions options}
34896      * used in the last {@link #load} operation. See {@link #load} for details
34897      * (defaults to <tt>null</tt>, in which case the {@link #lastOptions} are
34898      * used).</p>
34899      * <br><p>To add new params to the existing params:</p><pre><code>
34900 lastOptions = myStore.lastOptions;
34901 Ext.apply(lastOptions.params, {
34902     myNewParam: true
34903 });
34904 myStore.reload(lastOptions);
34905      * </code></pre>
34906      */
34907     reload : function(options){
34908         this.load(Ext.applyIf(options||{}, this.lastOptions));
34909     },
34910
34911     // private
34912     // Called as a callback by the Reader during a load operation.
34913     loadRecords : function(o, options, success){
34914         if (this.isDestroyed === true) {
34915             return;
34916         }
34917         if(!o || success === false){
34918             if(success !== false){
34919                 this.fireEvent('load', this, [], options);
34920             }
34921             if(options.callback){
34922                 options.callback.call(options.scope || this, [], options, false, o);
34923             }
34924             return;
34925         }
34926         var r = o.records, t = o.totalRecords || r.length;
34927         if(!options || options.add !== true){
34928             if(this.pruneModifiedRecords){
34929                 this.modified = [];
34930             }
34931             for(var i = 0, len = r.length; i < len; i++){
34932                 r[i].join(this);
34933             }
34934             if(this.snapshot){
34935                 this.data = this.snapshot;
34936                 delete this.snapshot;
34937             }
34938             this.clearData();
34939             this.data.addAll(r);
34940             this.totalLength = t;
34941             this.applySort();
34942             this.fireEvent('datachanged', this);
34943         }else{
34944             this.totalLength = Math.max(t, this.data.length+r.length);
34945             this.add(r);
34946         }
34947         this.fireEvent('load', this, r, options);
34948         if(options.callback){
34949             options.callback.call(options.scope || this, r, options, true);
34950         }
34951     },
34952
34953     /**
34954      * Loads data from a passed data block and fires the {@link #load} event. A {@link Ext.data.Reader Reader}
34955      * which understands the format of the data must have been configured in the constructor.
34956      * @param {Object} data The data block from which to read the Records.  The format of the data expected
34957      * is dependent on the type of {@link Ext.data.Reader Reader} that is configured and should correspond to
34958      * that {@link Ext.data.Reader Reader}'s <tt>{@link Ext.data.Reader#readRecords}</tt> parameter.
34959      * @param {Boolean} append (Optional) <tt>true</tt> to append the new Records rather the default to replace
34960      * the existing cache.
34961      * <b>Note</b>: that Records in a Store are keyed by their {@link Ext.data.Record#id id}, so added Records
34962      * with ids which are already present in the Store will <i>replace</i> existing Records. Only Records with
34963      * new, unique ids will be added.
34964      */
34965     loadData : function(o, append){
34966         var r = this.reader.readRecords(o);
34967         this.loadRecords(r, {add: append}, true);
34968     },
34969
34970     /**
34971      * Gets the number of cached records.
34972      * <p>If using paging, this may not be the total size of the dataset. If the data object
34973      * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
34974      * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
34975      * @return {Number} The number of Records in the Store's cache.
34976      */
34977     getCount : function(){
34978         return this.data.length || 0;
34979     },
34980
34981     /**
34982      * Gets the total number of records in the dataset as returned by the server.
34983      * <p>If using paging, for this to be accurate, the data object used by the {@link #reader Reader}
34984      * must contain the dataset size. For remote data sources, the value for this property
34985      * (<tt>totalProperty</tt> for {@link Ext.data.JsonReader JsonReader},
34986      * <tt>totalRecords</tt> for {@link Ext.data.XmlReader XmlReader}) shall be returned by a query on the server.
34987      * <b>Note</b>: see the Important note in {@link #load}.</p>
34988      * @return {Number} The number of Records as specified in the data object passed to the Reader
34989      * by the Proxy.
34990      * <p><b>Note</b>: this value is not updated when changing the contents of the Store locally.</p>
34991      */
34992     getTotalCount : function(){
34993         return this.totalLength || 0;
34994     },
34995
34996     /**
34997      * Returns an object describing the current sort state of this Store.
34998      * @return {Object} The sort state of the Store. An object with two properties:<ul>
34999      * <li><b>field : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
35000      * <li><b>direction : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
35001      * </ul>
35002      * See <tt>{@link #sortInfo}</tt> for additional details.
35003      */
35004     getSortState : function(){
35005         return this.sortInfo;
35006     },
35007
35008     /**
35009      * @private
35010      * Invokes sortData if we have sortInfo to sort on and are not sorting remotely
35011      */
35012     applySort : function(){
35013         if ((this.sortInfo || this.multiSortInfo) && !this.remoteSort) {
35014             this.sortData();
35015         }
35016     },
35017
35018     /**
35019      * @private
35020      * Performs the actual sorting of data. This checks to see if we currently have a multi sort or not. It applies
35021      * each sorter field/direction pair in turn by building an OR'ed master sorting function and running it against
35022      * the full dataset
35023      */
35024     sortData : function() {
35025         var sortInfo  = this.hasMultiSort ? this.multiSortInfo : this.sortInfo,
35026             direction = sortInfo.direction || "ASC",
35027             sorters   = sortInfo.sorters,
35028             sortFns   = [];
35029
35030         //if we just have a single sorter, pretend it's the first in an array
35031         if (!this.hasMultiSort) {
35032             sorters = [{direction: direction, field: sortInfo.field}];
35033         }
35034
35035         //create a sorter function for each sorter field/direction combo
35036         for (var i=0, j = sorters.length; i < j; i++) {
35037             sortFns.push(this.createSortFunction(sorters[i].field, sorters[i].direction));
35038         }
35039         
35040         if (sortFns.length == 0) {
35041             return;
35042         }
35043
35044         //the direction modifier is multiplied with the result of the sorting functions to provide overall sort direction
35045         //(as opposed to direction per field)
35046         var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
35047
35048         //create a function which ORs each sorter together to enable multi-sort
35049         var fn = function(r1, r2) {
35050           var result = sortFns[0].call(this, r1, r2);
35051
35052           //if we have more than one sorter, OR any additional sorter functions together
35053           if (sortFns.length > 1) {
35054               for (var i=1, j = sortFns.length; i < j; i++) {
35055                   result = result || sortFns[i].call(this, r1, r2);
35056               }
35057           }
35058
35059           return directionModifier * result;
35060         };
35061
35062         //sort the data
35063         this.data.sort(direction, fn);
35064         if (this.snapshot && this.snapshot != this.data) {
35065             this.snapshot.sort(direction, fn);
35066         }
35067     },
35068
35069     /**
35070      * @private
35071      * Creates and returns a function which sorts an array by the given field and direction
35072      * @param {String} field The field to create the sorter for
35073      * @param {String} direction The direction to sort by (defaults to "ASC")
35074      * @return {Function} A function which sorts by the field/direction combination provided
35075      */
35076     createSortFunction: function(field, direction) {
35077         direction = direction || "ASC";
35078         var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;
35079
35080         var sortType = this.fields.get(field).sortType;
35081
35082         //create a comparison function. Takes 2 records, returns 1 if record 1 is greater,
35083         //-1 if record 2 is greater or 0 if they are equal
35084         return function(r1, r2) {
35085             var v1 = sortType(r1.data[field]),
35086                 v2 = sortType(r2.data[field]);
35087
35088             return directionModifier * (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
35089         };
35090     },
35091
35092     /**
35093      * Sets the default sort column and order to be used by the next {@link #load} operation.
35094      * @param {String} fieldName The name of the field to sort by.
35095      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
35096      */
35097     setDefaultSort : function(field, dir) {
35098         dir = dir ? dir.toUpperCase() : 'ASC';
35099         this.sortInfo = {field: field, direction: dir};
35100         this.sortToggle[field] = dir;
35101     },
35102
35103     /**
35104      * Sort the Records.
35105      * If remote sorting is used, the sort is performed on the server, and the cache is reloaded. If local
35106      * sorting is used, the cache is sorted internally. See also {@link #remoteSort} and {@link #paramNames}.
35107      * This function accepts two call signatures - pass in a field name as the first argument to sort on a single
35108      * field, or pass in an array of sort configuration objects to sort by multiple fields.
35109      * Single sort example:
35110      * store.sort('name', 'ASC');
35111      * Multi sort example:
35112      * store.sort([
35113      *   {
35114      *     field    : 'name',
35115      *     direction: 'ASC'
35116      *   },
35117      *   {
35118      *     field    : 'salary',
35119      *     direction: 'DESC'
35120      *   }
35121      * ], 'ASC');
35122      * In this second form, the sort configs are applied in order, with later sorters sorting within earlier sorters' results.
35123      * For example, if two records with the same name are present they will also be sorted by salary if given the sort configs
35124      * above. Any number of sort configs can be added.
35125      * @param {String/Array} fieldName The name of the field to sort by, or an array of ordered sort configs
35126      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
35127      */
35128     sort : function(fieldName, dir) {
35129         if (Ext.isArray(arguments[0])) {
35130             return this.multiSort.call(this, fieldName, dir);
35131         } else {
35132             return this.singleSort(fieldName, dir);
35133         }
35134     },
35135
35136     /**
35137      * Sorts the store contents by a single field and direction. This is called internally by {@link sort} and would
35138      * not usually be called manually
35139      * @param {String} fieldName The name of the field to sort by.
35140      * @param {String} dir (optional) The sort order, 'ASC' or 'DESC' (case-sensitive, defaults to <tt>'ASC'</tt>)
35141      */
35142     singleSort: function(fieldName, dir) {
35143         var field = this.fields.get(fieldName);
35144         if (!field) return false;
35145
35146         var name       = field.name,
35147             sortInfo   = this.sortInfo || null,
35148             sortToggle = this.sortToggle ? this.sortToggle[name] : null;
35149
35150         if (!dir) {
35151             if (sortInfo && sortInfo.field == name) { // toggle sort dir
35152                 dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
35153             } else {
35154                 dir = field.sortDir;
35155             }
35156         }
35157
35158         this.sortToggle[name] = dir;
35159         this.sortInfo = {field: name, direction: dir};
35160         this.hasMultiSort = false;
35161
35162         if (this.remoteSort) {
35163             if (!this.load(this.lastOptions)) {
35164                 if (sortToggle) {
35165                     this.sortToggle[name] = sortToggle;
35166                 }
35167                 if (sortInfo) {
35168                     this.sortInfo = sortInfo;
35169                 }
35170             }
35171         } else {
35172             this.applySort();
35173             this.fireEvent('datachanged', this);
35174         }
35175     },
35176
35177     /**
35178      * Sorts the contents of this store by multiple field/direction sorters. This is called internally by {@link sort}
35179      * and would not usually be called manually.
35180      * Multi sorting only currently applies to local datasets - multiple sort data is not currently sent to a proxy
35181      * if remoteSort is used.
35182      * @param {Array} sorters Array of sorter objects (field and direction)
35183      * @param {String} direction Overall direction to sort the ordered results by (defaults to "ASC")
35184      */
35185     multiSort: function(sorters, direction) {
35186         this.hasMultiSort = true;
35187         direction = direction || "ASC";
35188
35189         //toggle sort direction
35190         if (this.multiSortInfo && direction == this.multiSortInfo.direction) {
35191             direction = direction.toggle("ASC", "DESC");
35192         }
35193
35194         /**
35195          * @property multiSortInfo
35196          * @type Object
35197          * Object containing overall sort direction and an ordered array of sorter configs used when sorting on multiple fields
35198          */
35199         this.multiSortInfo = {
35200             sorters  : sorters,
35201             direction: direction
35202         };
35203         
35204         if (this.remoteSort) {
35205             this.singleSort(sorters[0].field, sorters[0].direction);
35206
35207         } else {
35208             this.applySort();
35209             this.fireEvent('datachanged', this);
35210         }
35211     },
35212
35213     /**
35214      * Calls the specified function for each of the {@link Ext.data.Record Records} in the cache.
35215      * @param {Function} fn The function to call. The {@link Ext.data.Record Record} is passed as the first parameter.
35216      * Returning <tt>false</tt> aborts and exits the iteration.
35217      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
35218      * Defaults to the current {@link Ext.data.Record Record} in the iteration.
35219      */
35220     each : function(fn, scope){
35221         this.data.each(fn, scope);
35222     },
35223
35224     /**
35225      * Gets all {@link Ext.data.Record records} modified since the last commit.  Modified records are
35226      * persisted across load operations (e.g., during paging). <b>Note</b>: deleted records are not
35227      * included.  See also <tt>{@link #pruneModifiedRecords}</tt> and
35228      * {@link Ext.data.Record}<tt>{@link Ext.data.Record#markDirty markDirty}.</tt>.
35229      * @return {Ext.data.Record[]} An array of {@link Ext.data.Record Records} containing outstanding
35230      * modifications.  To obtain modified fields within a modified record see
35231      *{@link Ext.data.Record}<tt>{@link Ext.data.Record#modified modified}.</tt>.
35232      */
35233     getModifiedRecords : function(){
35234         return this.modified;
35235     },
35236
35237     /**
35238      * Sums the value of <tt>property</tt> for each {@link Ext.data.Record record} between <tt>start</tt>
35239      * and <tt>end</tt> and returns the result.
35240      * @param {String} property A field in each record
35241      * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
35242      * @param {Number} end (optional) The last record index to include (defaults to length - 1)
35243      * @return {Number} The sum
35244      */
35245     sum : function(property, start, end){
35246         var rs = this.data.items, v = 0;
35247         start = start || 0;
35248         end = (end || end === 0) ? end : rs.length-1;
35249
35250         for(var i = start; i <= end; i++){
35251             v += (rs[i].data[property] || 0);
35252         }
35253         return v;
35254     },
35255
35256     /**
35257      * @private
35258      * Returns a filter function used to test a the given property's value. Defers most of the work to
35259      * Ext.util.MixedCollection's createValueMatcher function
35260      * @param {String} property The property to create the filter function for
35261      * @param {String/RegExp} value The string/regex to compare the property value to
35262      * @param {Boolean} anyMatch True if we don't care if the filter value is not the full value (defaults to false)
35263      * @param {Boolean} caseSensitive True to create a case-sensitive regex (defaults to false)
35264      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
35265      */
35266     createFilterFn : function(property, value, anyMatch, caseSensitive, exactMatch){
35267         if(Ext.isEmpty(value, false)){
35268             return false;
35269         }
35270         value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
35271         return function(r) {
35272             return value.test(r.data[property]);
35273         };
35274     },
35275
35276     /**
35277      * @private
35278      * Given an array of filter functions (each with optional scope), constructs and returns a single function that returns
35279      * the result of all of the filters ANDed together
35280      * @param {Array} filters The array of filter objects (each object should contain an 'fn' and optional scope)
35281      * @return {Function} The multiple filter function
35282      */
35283     createMultipleFilterFn: function(filters) {
35284         return function(record) {
35285             var isMatch = true;
35286
35287             for (var i=0, j = filters.length; i < j; i++) {
35288                 var filter = filters[i],
35289                     fn     = filter.fn,
35290                     scope  = filter.scope;
35291
35292                 isMatch = isMatch && fn.call(scope, record);
35293             }
35294
35295             return isMatch;
35296         };
35297     },
35298
35299     /**
35300      * Filter the {@link Ext.data.Record records} by a specified property. Alternatively, pass an array of filter
35301      * options to filter by more than one property.
35302      * Single filter example:
35303      * store.filter('name', 'Ed', true, true); //finds all records containing the substring 'Ed'
35304      * Multiple filter example:
35305      * <pre><code>
35306      * store.filter([
35307      *   {
35308      *     property     : 'name',
35309      *     value        : 'Ed',
35310      *     anyMatch     : true, //optional, defaults to true
35311      *     caseSensitive: true  //optional, defaults to true
35312      *   },
35313      *
35314      *   //filter functions can also be passed
35315      *   {
35316      *     fn   : function(record) {
35317      *       return record.get('age') == 24
35318      *     },
35319      *     scope: this
35320      *   }
35321      * ]);
35322      * </code></pre>
35323      * @param {String|Array} field A field on your records, or an array containing multiple filter options
35324      * @param {String/RegExp} value Either a string that the field should begin with, or a RegExp to test
35325      * against the field.
35326      * @param {Boolean} anyMatch (optional) <tt>true</tt> to match any part not just the beginning
35327      * @param {Boolean} caseSensitive (optional) <tt>true</tt> for case sensitive comparison
35328      * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
35329      */
35330     filter : function(property, value, anyMatch, caseSensitive, exactMatch){
35331         //we can accept an array of filter objects, or a single filter object - normalize them here
35332         if (Ext.isObject(property)) {
35333             property = [property];
35334         }
35335
35336         if (Ext.isArray(property)) {
35337             var filters = [];
35338
35339             //normalize the filters passed into an array of filter functions
35340             for (var i=0, j = property.length; i < j; i++) {
35341                 var filter = property[i],
35342                     func   = filter.fn,
35343                     scope  = filter.scope || this;
35344
35345                 //if we weren't given a filter function, construct one now
35346                 if (!Ext.isFunction(func)) {
35347                     func = this.createFilterFn(filter.property, filter.value, filter.anyMatch, filter.caseSensitive, filter.exactMatch);
35348                 }
35349
35350                 filters.push({fn: func, scope: scope});
35351             }
35352
35353             var fn = this.createMultipleFilterFn(filters);
35354         } else {
35355             //classic single property filter
35356             var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
35357         }
35358
35359         return fn ? this.filterBy(fn) : this.clearFilter();
35360     },
35361
35362     /**
35363      * Filter by a function. The specified function will be called for each
35364      * Record in this Store. If the function returns <tt>true</tt> the Record is included,
35365      * otherwise it is filtered out.
35366      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
35367      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
35368      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
35369      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
35370      * </ul>
35371      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
35372      */
35373     filterBy : function(fn, scope){
35374         this.snapshot = this.snapshot || this.data;
35375         this.data = this.queryBy(fn, scope||this);
35376         this.fireEvent('datachanged', this);
35377     },
35378
35379     /**
35380      * Revert to a view of the Record cache with no filtering applied.
35381      * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
35382      * {@link #datachanged} event.
35383      */
35384     clearFilter : function(suppressEvent){
35385         if(this.isFiltered()){
35386             this.data = this.snapshot;
35387             delete this.snapshot;
35388             if(suppressEvent !== true){
35389                 this.fireEvent('datachanged', this);
35390             }
35391         }
35392     },
35393
35394     /**
35395      * Returns true if this store is currently filtered
35396      * @return {Boolean}
35397      */
35398     isFiltered : function(){
35399         return !!this.snapshot && this.snapshot != this.data;
35400     },
35401
35402     /**
35403      * Query the records by a specified property.
35404      * @param {String} field A field on your records
35405      * @param {String/RegExp} value Either a string that the field
35406      * should begin with, or a RegExp to test against the field.
35407      * @param {Boolean} anyMatch (optional) True to match any part not just the beginning
35408      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
35409      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
35410      */
35411     query : function(property, value, anyMatch, caseSensitive){
35412         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
35413         return fn ? this.queryBy(fn) : this.data.clone();
35414     },
35415
35416     /**
35417      * Query the cached records in this Store using a filtering function. The specified function
35418      * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
35419      * included in the results.
35420      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
35421      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
35422      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
35423      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
35424      * </ul>
35425      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
35426      * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
35427      **/
35428     queryBy : function(fn, scope){
35429         var data = this.snapshot || this.data;
35430         return data.filterBy(fn, scope||this);
35431     },
35432
35433     /**
35434      * Finds the index of the first matching Record in this store by a specific field value.
35435      * @param {String} fieldName The name of the Record field to test.
35436      * @param {String/RegExp} value Either a string that the field value
35437      * should begin with, or a RegExp to test against the field.
35438      * @param {Number} startIndex (optional) The index to start searching at
35439      * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
35440      * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
35441      * @return {Number} The matched index or -1
35442      */
35443     find : function(property, value, start, anyMatch, caseSensitive){
35444         var fn = this.createFilterFn(property, value, anyMatch, caseSensitive);
35445         return fn ? this.data.findIndexBy(fn, null, start) : -1;
35446     },
35447
35448     /**
35449      * Finds the index of the first matching Record in this store by a specific field value.
35450      * @param {String} fieldName The name of the Record field to test.
35451      * @param {Mixed} value The value to match the field against.
35452      * @param {Number} startIndex (optional) The index to start searching at
35453      * @return {Number} The matched index or -1
35454      */
35455     findExact: function(property, value, start){
35456         return this.data.findIndexBy(function(rec){
35457             return rec.get(property) === value;
35458         }, this, start);
35459     },
35460
35461     /**
35462      * Find the index of the first matching Record in this Store by a function.
35463      * If the function returns <tt>true</tt> it is considered a match.
35464      * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
35465      * <li><b>record</b> : Ext.data.Record<p class="sub-desc">The {@link Ext.data.Record record}
35466      * to test for filtering. Access field values using {@link Ext.data.Record#get}.</p></li>
35467      * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
35468      * </ul>
35469      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
35470      * @param {Number} startIndex (optional) The index to start searching at
35471      * @return {Number} The matched index or -1
35472      */
35473     findBy : function(fn, scope, start){
35474         return this.data.findIndexBy(fn, scope, start);
35475     },
35476
35477     /**
35478      * Collects unique values for a particular dataIndex from this store.
35479      * @param {String} dataIndex The property to collect
35480      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
35481      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
35482      * @return {Array} An array of the unique values
35483      **/
35484     collect : function(dataIndex, allowNull, bypassFilter){
35485         var d = (bypassFilter === true && this.snapshot) ?
35486                 this.snapshot.items : this.data.items;
35487         var v, sv, r = [], l = {};
35488         for(var i = 0, len = d.length; i < len; i++){
35489             v = d[i].data[dataIndex];
35490             sv = String(v);
35491             if((allowNull || !Ext.isEmpty(v)) && !l[sv]){
35492                 l[sv] = true;
35493                 r[r.length] = v;
35494             }
35495         }
35496         return r;
35497     },
35498
35499     // private
35500     afterEdit : function(record){
35501         if(this.modified.indexOf(record) == -1){
35502             this.modified.push(record);
35503         }
35504         this.fireEvent('update', this, record, Ext.data.Record.EDIT);
35505     },
35506
35507     // private
35508     afterReject : function(record){
35509         this.modified.remove(record);
35510         this.fireEvent('update', this, record, Ext.data.Record.REJECT);
35511     },
35512
35513     // private
35514     afterCommit : function(record){
35515         this.modified.remove(record);
35516         this.fireEvent('update', this, record, Ext.data.Record.COMMIT);
35517     },
35518
35519     /**
35520      * Commit all Records with {@link #getModifiedRecords outstanding changes}. To handle updates for changes,
35521      * subscribe to the Store's {@link #update update event}, and perform updating when the third parameter is
35522      * Ext.data.Record.COMMIT.
35523      */
35524     commitChanges : function(){
35525         var m = this.modified.slice(0);
35526         this.modified = [];
35527         for(var i = 0, len = m.length; i < len; i++){
35528             m[i].commit();
35529         }
35530     },
35531
35532     /**
35533      * {@link Ext.data.Record#reject Reject} outstanding changes on all {@link #getModifiedRecords modified records}.
35534      */
35535     rejectChanges : function(){
35536         var m = this.modified.slice(0);
35537         this.modified = [];
35538         for(var i = 0, len = m.length; i < len; i++){
35539             m[i].reject();
35540         }
35541         var m = this.removed.slice(0).reverse();
35542         this.removed = [];
35543         for(var i = 0, len = m.length; i < len; i++){
35544             this.insert(m[i].lastIndex||0, m[i]);
35545             m[i].reject();
35546         }
35547     },
35548
35549     // private
35550     onMetaChange : function(meta){
35551         this.recordType = this.reader.recordType;
35552         this.fields = this.recordType.prototype.fields;
35553         delete this.snapshot;
35554         if(this.reader.meta.sortInfo){
35555             this.sortInfo = this.reader.meta.sortInfo;
35556         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
35557             delete this.sortInfo;
35558         }
35559         if(this.writer){
35560             this.writer.meta = this.reader.meta;
35561         }
35562         this.modified = [];
35563         this.fireEvent('metachange', this, this.reader.meta);
35564     },
35565
35566     // private
35567     findInsertIndex : function(record){
35568         this.suspendEvents();
35569         var data = this.data.clone();
35570         this.data.add(record);
35571         this.applySort();
35572         var index = this.data.indexOf(record);
35573         this.data = data;
35574         this.resumeEvents();
35575         return index;
35576     },
35577
35578     /**
35579      * Set the value for a property name in this store's {@link #baseParams}.  Usage:</p><pre><code>
35580 myStore.setBaseParam('foo', {bar:3});
35581 </code></pre>
35582      * @param {String} name Name of the property to assign
35583      * @param {Mixed} value Value to assign the <tt>name</tt>d property
35584      **/
35585     setBaseParam : function (name, value){
35586         this.baseParams = this.baseParams || {};
35587         this.baseParams[name] = value;
35588     }
35589 });
35590
35591 Ext.reg('store', Ext.data.Store);
35592
35593 /**
35594  * @class Ext.data.Store.Error
35595  * @extends Ext.Error
35596  * Store Error extension.
35597  * @param {String} name
35598  */
35599 Ext.data.Store.Error = Ext.extend(Ext.Error, {
35600     name: 'Ext.data.Store'
35601 });
35602 Ext.apply(Ext.data.Store.Error.prototype, {
35603     lang: {
35604         'writer-undefined' : 'Attempted to execute a write-action without a DataWriter installed.'
35605     }
35606 });
35607 /**
35608  * @class Ext.data.Field
35609  * <p>This class encapsulates the field definition information specified in the field definition objects
35610  * passed to {@link Ext.data.Record#create}.</p>
35611  * <p>Developers do not need to instantiate this class. Instances are created by {@link Ext.data.Record.create}
35612  * and cached in the {@link Ext.data.Record#fields fields} property of the created Record constructor's <b>prototype.</b></p>
35613  */
35614 Ext.data.Field = Ext.extend(Object, {
35615     
35616     constructor : function(config){
35617         if(Ext.isString(config)){
35618             config = {name: config};
35619         }
35620         Ext.apply(this, config);
35621         
35622         var types = Ext.data.Types,
35623             st = this.sortType,
35624             t;
35625
35626         if(this.type){
35627             if(Ext.isString(this.type)){
35628                 this.type = Ext.data.Types[this.type.toUpperCase()] || types.AUTO;
35629             }
35630         }else{
35631             this.type = types.AUTO;
35632         }
35633
35634         // named sortTypes are supported, here we look them up
35635         if(Ext.isString(st)){
35636             this.sortType = Ext.data.SortTypes[st];
35637         }else if(Ext.isEmpty(st)){
35638             this.sortType = this.type.sortType;
35639         }
35640
35641         if(!this.convert){
35642             this.convert = this.type.convert;
35643         }
35644     },
35645     
35646     /**
35647      * @cfg {String} name
35648      * The name by which the field is referenced within the Record. This is referenced by, for example,
35649      * the <code>dataIndex</code> property in column definition objects passed to {@link Ext.grid.ColumnModel}.
35650      * <p>Note: In the simplest case, if no properties other than <code>name</code> are required, a field
35651      * definition may consist of just a String for the field name.</p>
35652      */
35653     /**
35654      * @cfg {Mixed} type
35655      * (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>
35656      * has not been specified. This may be specified as a string value. Possible values are
35657      * <div class="mdetail-params"><ul>
35658      * <li>auto (Default, implies no conversion)</li>
35659      * <li>string</li>
35660      * <li>int</li>
35661      * <li>float</li>
35662      * <li>boolean</li>
35663      * <li>date</li></ul></div>
35664      * <p>This may also be specified by referencing a member of the {@link Ext.data.Types} class.</p>
35665      * <p>Developers may create their own application-specific data types by defining new members of the
35666      * {@link Ext.data.Types} class.</p>
35667      */
35668     /**
35669      * @cfg {Function} convert
35670      * (Optional) A function which converts the value provided by the Reader into an object that will be stored
35671      * in the Record. It is passed the following parameters:<div class="mdetail-params"><ul>
35672      * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
35673      * the configured <code>{@link Ext.data.Field#defaultValue defaultValue}</code>.</div></li>
35674      * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
35675      * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
35676      *  ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
35677      * </ul></div>
35678      * <pre><code>
35679 // example of convert function
35680 function fullName(v, record){
35681     return record.name.last + ', ' + record.name.first;
35682 }
35683
35684 function location(v, record){
35685     return !record.city ? '' : (record.city + ', ' + record.state);
35686 }
35687
35688 var Dude = Ext.data.Record.create([
35689     {name: 'fullname',  convert: fullName},
35690     {name: 'firstname', mapping: 'name.first'},
35691     {name: 'lastname',  mapping: 'name.last'},
35692     {name: 'city', defaultValue: 'homeless'},
35693     'state',
35694     {name: 'location',  convert: location}
35695 ]);
35696
35697 // create the data store
35698 var store = new Ext.data.Store({
35699     reader: new Ext.data.JsonReader(
35700         {
35701             idProperty: 'key',
35702             root: 'daRoot',
35703             totalProperty: 'total'
35704         },
35705         Dude  // recordType
35706     )
35707 });
35708
35709 var myData = [
35710     { key: 1,
35711       name: { first: 'Fat',    last:  'Albert' }
35712       // notice no city, state provided in data object
35713     },
35714     { key: 2,
35715       name: { first: 'Barney', last:  'Rubble' },
35716       city: 'Bedrock', state: 'Stoneridge'
35717     },
35718     { key: 3,
35719       name: { first: 'Cliff',  last:  'Claven' },
35720       city: 'Boston',  state: 'MA'
35721     }
35722 ];
35723      * </code></pre>
35724      */
35725     /**
35726      * @cfg {String} dateFormat
35727      * <p>(Optional) Used when converting received data into a Date when the {@link #type} is specified as <code>"date"</code>.</p>
35728      * <p>A format string for the {@link Date#parseDate Date.parseDate} function, or "timestamp" if the
35729      * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
35730      * javascript millisecond timestamp. See {@link Date}</p>
35731      */
35732     dateFormat: null,
35733     /**
35734      * @cfg {Mixed} defaultValue
35735      * (Optional) The default value used <b>when a Record is being created by a {@link Ext.data.Reader Reader}</b>
35736      * when the item referenced by the <code>{@link Ext.data.Field#mapping mapping}</code> does not exist in the data
35737      * object (i.e. undefined). (defaults to "")
35738      */
35739     defaultValue: "",
35740     /**
35741      * @cfg {String/Number} mapping
35742      * <p>(Optional) A path expression for use by the {@link Ext.data.DataReader} implementation
35743      * that is creating the {@link Ext.data.Record Record} to extract the Field value from the data object.
35744      * If the path expression is the same as the field name, the mapping may be omitted.</p>
35745      * <p>The form of the mapping expression depends on the Reader being used.</p>
35746      * <div class="mdetail-params"><ul>
35747      * <li>{@link Ext.data.JsonReader}<div class="sub-desc">The mapping is a string containing the javascript
35748      * 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>
35749      * <li>{@link Ext.data.XmlReader}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
35750      * item relative to the DOM element that represents the {@link Ext.data.XmlReader#record record}. Defaults to the field name.</div></li>
35751      * <li>{@link Ext.data.ArrayReader}<div class="sub-desc">The mapping is a number indicating the Array index
35752      * of the field's value. Defaults to the field specification's Array position.</div></li>
35753      * </ul></div>
35754      * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
35755      * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
35756      * return the desired data.</p>
35757      */
35758     mapping: null,
35759     /**
35760      * @cfg {Function} sortType
35761      * (Optional) A function which converts a Field's value to a comparable value in order to ensure
35762      * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
35763      * sort example:<pre><code>
35764 // current sort     after sort we want
35765 // +-+------+          +-+------+
35766 // |1|First |          |1|First |
35767 // |2|Last  |          |3|Second|
35768 // |3|Second|          |2|Last  |
35769 // +-+------+          +-+------+
35770
35771 sortType: function(value) {
35772    switch (value.toLowerCase()) // native toLowerCase():
35773    {
35774       case 'first': return 1;
35775       case 'second': return 2;
35776       default: return 3;
35777    }
35778 }
35779      * </code></pre>
35780      */
35781     sortType : null,
35782     /**
35783      * @cfg {String} sortDir
35784      * (Optional) Initial direction to sort (<code>"ASC"</code> or  <code>"DESC"</code>).  Defaults to
35785      * <code>"ASC"</code>.
35786      */
35787     sortDir : "ASC",
35788     /**
35789      * @cfg {Boolean} allowBlank
35790      * (Optional) Used for validating a {@link Ext.data.Record record}, defaults to <code>true</code>.
35791      * An empty value here will cause {@link Ext.data.Record}.{@link Ext.data.Record#isValid isValid}
35792      * to evaluate to <code>false</code>.
35793      */
35794     allowBlank : true
35795 });
35796 /**
35797  * @class Ext.data.DataReader
35798  * Abstract base class for reading structured data from a data source and converting
35799  * it into an object containing {@link Ext.data.Record} objects and metadata for use
35800  * by an {@link Ext.data.Store}.  This class is intended to be extended and should not
35801  * be created directly. For existing implementations, see {@link Ext.data.ArrayReader},
35802  * {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}.
35803  * @constructor Create a new DataReader
35804  * @param {Object} meta Metadata configuration options (implementation-specific).
35805  * @param {Array/Object} recordType
35806  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
35807  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
35808  * constructor created using {@link Ext.data.Record#create}.</p>
35809  */
35810 Ext.data.DataReader = function(meta, recordType){
35811     /**
35812      * This DataReader's configured metadata as passed to the constructor.
35813      * @type Mixed
35814      * @property meta
35815      */
35816     this.meta = meta;
35817     /**
35818      * @cfg {Array/Object} fields
35819      * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
35820      * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
35821      * constructor created from {@link Ext.data.Record#create}.</p>
35822      */
35823     this.recordType = Ext.isArray(recordType) ?
35824         Ext.data.Record.create(recordType) : recordType;
35825
35826     // if recordType defined make sure extraction functions are defined
35827     if (this.recordType){
35828         this.buildExtractors();
35829     }
35830 };
35831
35832 Ext.data.DataReader.prototype = {
35833     /**
35834      * @cfg {String} messageProperty [undefined] Optional name of a property within a server-response that represents a user-feedback message.
35835      */
35836     /**
35837      * Abstract method created in extension's buildExtractors impl.
35838      */
35839     getTotal: Ext.emptyFn,
35840     /**
35841      * Abstract method created in extension's buildExtractors impl.
35842      */
35843     getRoot: Ext.emptyFn,
35844     /**
35845      * Abstract method created in extension's buildExtractors impl.
35846      */
35847     getMessage: Ext.emptyFn,
35848     /**
35849      * Abstract method created in extension's buildExtractors impl.
35850      */
35851     getSuccess: Ext.emptyFn,
35852     /**
35853      * Abstract method created in extension's buildExtractors impl.
35854      */
35855     getId: Ext.emptyFn,
35856     /**
35857      * Abstract method, overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}
35858      */
35859     buildExtractors : Ext.emptyFn,
35860     /**
35861      * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}
35862      */
35863     extractValues : Ext.emptyFn,
35864
35865     /**
35866      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.
35867      * You <b>must</b> return at least the database pk using the idProperty defined in your DataReader configuration.  The incoming
35868      * data from server will be merged with the data in the local record.
35869      * In addition, you <b>must</b> return record-data from the server in the same order received.
35870      * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed.
35871      * @param {Record/Record[]} record The phantom record to be realized.
35872      * @param {Object/Object[]} data The new record data to apply.  Must include the primary-key from database defined in idProperty field.
35873      */
35874     realize: function(rs, data){
35875         if (Ext.isArray(rs)) {
35876             for (var i = rs.length - 1; i >= 0; i--) {
35877                 // recurse
35878                 if (Ext.isArray(data)) {
35879                     this.realize(rs.splice(i,1).shift(), data.splice(i,1).shift());
35880                 }
35881                 else {
35882                     // weird...rs is an array but data isn't??  recurse but just send in the whole invalid data object.
35883                     // the else clause below will detect !this.isData and throw exception.
35884                     this.realize(rs.splice(i,1).shift(), data);
35885                 }
35886             }
35887         }
35888         else {
35889             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.
35890             if (Ext.isArray(data) && data.length == 1) {
35891                 data = data.shift();
35892             }
35893             if (!this.isData(data)) {
35894                 // TODO: Let exception-handler choose to commit or not rather than blindly rs.commit() here.
35895                 //rs.commit();
35896                 throw new Ext.data.DataReader.Error('realize', rs);
35897             }
35898             rs.phantom = false; // <-- That's what it's all about
35899             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords
35900             rs.id = this.getId(data);
35901             rs.data = data;
35902
35903             rs.commit();
35904         }
35905     },
35906
35907     /**
35908      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.
35909      * If returning data from multiple-records after a batch-update, you <b>must</b> return record-data from the server in
35910      * the same order received.  Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be
35911      * suppressed as the record receives fresh new data-hash
35912      * @param {Record/Record[]} rs
35913      * @param {Object/Object[]} data
35914      */
35915     update : function(rs, data) {
35916         if (Ext.isArray(rs)) {
35917             for (var i=rs.length-1; i >= 0; i--) {
35918                 if (Ext.isArray(data)) {
35919                     this.update(rs.splice(i,1).shift(), data.splice(i,1).shift());
35920                 }
35921                 else {
35922                     // weird...rs is an array but data isn't??  recurse but just send in the whole data object.
35923                     // the else clause below will detect !this.isData and throw exception.
35924                     this.update(rs.splice(i,1).shift(), data);
35925                 }
35926             }
35927         }
35928         else {
35929             // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.
35930             if (Ext.isArray(data) && data.length == 1) {
35931                 data = data.shift();
35932             }
35933             if (this.isData(data)) {
35934                 rs.data = Ext.apply(rs.data, data);
35935             }
35936             rs.commit();
35937         }
35938     },
35939
35940     /**
35941      * returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
35942      * @param {Object[]/Object} data-root from server response
35943      * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record
35944      * @private
35945      */
35946     extractData : function(root, returnRecords) {
35947         // A bit ugly this, too bad the Record's raw data couldn't be saved in a common property named "raw" or something.
35948         var rawName = (this instanceof Ext.data.JsonReader) ? 'json' : 'node';
35949
35950         var rs = [];
35951
35952         // Had to add Check for XmlReader, #isData returns true if root is an Xml-object.  Want to check in order to re-factor
35953         // #extractData into DataReader base, since the implementations are almost identical for JsonReader, XmlReader
35954         if (this.isData(root) && !(this instanceof Ext.data.XmlReader)) {
35955             root = [root];
35956         }
35957         var f       = this.recordType.prototype.fields,
35958             fi      = f.items,
35959             fl      = f.length,
35960             rs      = [];
35961         if (returnRecords === true) {
35962             var Record = this.recordType;
35963             for (var i = 0; i < root.length; i++) {
35964                 var n = root[i];
35965                 var record = new Record(this.extractValues(n, fi, fl), this.getId(n));
35966                 record[rawName] = n;    // <-- There's implementation of ugly bit, setting the raw record-data.
35967                 rs.push(record);
35968             }
35969         }
35970         else {
35971             for (var i = 0; i < root.length; i++) {
35972                 var data = this.extractValues(root[i], fi, fl);
35973                 data[this.meta.idProperty] = this.getId(root[i]);
35974                 rs.push(data);
35975             }
35976         }
35977         return rs;
35978     },
35979
35980     /**
35981      * Returns true if the supplied data-hash <b>looks</b> and quacks like data.  Checks to see if it has a key
35982      * corresponding to idProperty defined in your DataReader config containing non-empty pk.
35983      * @param {Object} data
35984      * @return {Boolean}
35985      */
35986     isData : function(data) {
35987         return (data && Ext.isObject(data) && !Ext.isEmpty(this.getId(data))) ? true : false;
35988     },
35989
35990     // private function a store will createSequence upon
35991     onMetaChange : function(meta){
35992         delete this.ef;
35993         this.meta = meta;
35994         this.recordType = Ext.data.Record.create(meta.fields);
35995         this.buildExtractors();
35996     }
35997 };
35998
35999 /**
36000  * @class Ext.data.DataReader.Error
36001  * @extends Ext.Error
36002  * General error class for Ext.data.DataReader
36003  */
36004 Ext.data.DataReader.Error = Ext.extend(Ext.Error, {
36005     constructor : function(message, arg) {
36006         this.arg = arg;
36007         Ext.Error.call(this, message);
36008     },
36009     name: 'Ext.data.DataReader'
36010 });
36011 Ext.apply(Ext.data.DataReader.Error.prototype, {
36012     lang : {
36013         'update': "#update received invalid data from server.  Please see docs for DataReader#update and review your DataReader configuration.",
36014         'realize': "#realize was called with invalid remote-data.  Please see the docs for DataReader#realize and review your DataReader configuration.",
36015         'invalid-response': "#readResponse received an invalid response from the server."
36016     }
36017 });
36018 /**
36019  * @class Ext.data.DataWriter
36020  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
36021  * an Ext.data.Store and a server-side framework. A Writer enabled Store will
36022  * automatically manage the Ajax requests to perform CRUD actions on a Store.</p>
36023  * <p>Ext.data.DataWriter is an abstract base class which is intended to be extended
36024  * and should not be created directly. For existing implementations, see
36025  * {@link Ext.data.JsonWriter}.</p>
36026  * <p>Creating a writer is simple:</p>
36027  * <pre><code>
36028 var writer = new Ext.data.JsonWriter({
36029     encode: false   // &lt;--- false causes data to be printed to jsonData config-property of Ext.Ajax#reqeust
36030 });
36031  * </code></pre>
36032  * * <p>Same old JsonReader as Ext-2.x:</p>
36033  * <pre><code>
36034 var reader = new Ext.data.JsonReader({idProperty: 'id'}, [{name: 'first'}, {name: 'last'}, {name: 'email'}]);
36035  * </code></pre>
36036  *
36037  * <p>The proxy for a writer enabled store can be configured with a simple <code>url</code>:</p>
36038  * <pre><code>
36039 // Create a standard HttpProxy instance.
36040 var proxy = new Ext.data.HttpProxy({
36041     url: 'app.php/users'    // &lt;--- Supports "provides"-type urls, such as '/users.json', '/products.xml' (Hello Rails/Merb)
36042 });
36043  * </code></pre>
36044  * <p>For finer grained control, the proxy may also be configured with an <code>API</code>:</p>
36045  * <pre><code>
36046 // Maximum flexibility with the API-configuration
36047 var proxy = new Ext.data.HttpProxy({
36048     api: {
36049         read    : 'app.php/users/read',
36050         create  : 'app.php/users/create',
36051         update  : 'app.php/users/update',
36052         destroy : {  // &lt;--- Supports object-syntax as well
36053             url: 'app.php/users/destroy',
36054             method: "DELETE"
36055         }
36056     }
36057 });
36058  * </code></pre>
36059  * <p>Pulling it all together into a Writer-enabled Store:</p>
36060  * <pre><code>
36061 var store = new Ext.data.Store({
36062     proxy: proxy,
36063     reader: reader,
36064     writer: writer,
36065     autoLoad: true,
36066     autoSave: true  // -- Cell-level updates.
36067 });
36068  * </code></pre>
36069  * <p>Initiating write-actions <b>automatically</b>, using the existing Ext2.0 Store/Record API:</p>
36070  * <pre><code>
36071 var rec = store.getAt(0);
36072 rec.set('email', 'foo@bar.com');  // &lt;--- Immediately initiates an UPDATE action through configured proxy.
36073
36074 store.remove(rec);  // &lt;---- Immediately initiates a DESTROY action through configured proxy.
36075  * </code></pre>
36076  * <p>For <b>record/batch</b> updates, use the Store-configuration {@link Ext.data.Store#autoSave autoSave:false}</p>
36077  * <pre><code>
36078 var store = new Ext.data.Store({
36079     proxy: proxy,
36080     reader: reader,
36081     writer: writer,
36082     autoLoad: true,
36083     autoSave: false  // -- disable cell-updates
36084 });
36085
36086 var urec = store.getAt(0);
36087 urec.set('email', 'foo@bar.com');
36088
36089 var drec = store.getAt(1);
36090 store.remove(drec);
36091
36092 // Push the button!
36093 store.save();
36094  * </code></pre>
36095  * @constructor Create a new DataWriter
36096  * @param {Object} meta Metadata configuration options (implementation-specific)
36097  * @param {Object} recordType Either an Array of field definition objects as specified
36098  * in {@link Ext.data.Record#create}, or an {@link Ext.data.Record} object created
36099  * using {@link Ext.data.Record#create}.
36100  */
36101 Ext.data.DataWriter = function(config){
36102     Ext.apply(this, config);
36103 };
36104 Ext.data.DataWriter.prototype = {
36105
36106     /**
36107      * @cfg {Boolean} writeAllFields
36108      * <tt>false</tt> by default.  Set <tt>true</tt> to have DataWriter return ALL fields of a modified
36109      * record -- not just those that changed.
36110      * <tt>false</tt> to have DataWriter only request modified fields from a record.
36111      */
36112     writeAllFields : false,
36113     /**
36114      * @cfg {Boolean} listful
36115      * <tt>false</tt> by default.  Set <tt>true</tt> to have the DataWriter <b>always</b> write HTTP params as a list,
36116      * even when acting upon a single record.
36117      */
36118     listful : false,    // <-- listful is actually not used internally here in DataWriter.  @see Ext.data.Store#execute.
36119
36120     /**
36121      * 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},
36122      * Where the first parameter is the <i>receiver</i> of paramaters and the second, baseParams, <i>the source</i>.
36123      * @param {Object} params The request-params receiver.
36124      * @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}.
36125      * @param {String} action [{@link Ext.data.Api#actions create|update|destroy}]
36126      * @param {Record/Record[]} rs The recordset to write, the subject(s) of the write action.
36127      */
36128     apply : function(params, baseParams, action, rs) {
36129         var data    = [],
36130         renderer    = action + 'Record';
36131         // TODO implement @cfg listful here
36132         if (Ext.isArray(rs)) {
36133             Ext.each(rs, function(rec){
36134                 data.push(this[renderer](rec));
36135             }, this);
36136         }
36137         else if (rs instanceof Ext.data.Record) {
36138             data = this[renderer](rs);
36139         }
36140         this.render(params, baseParams, data);
36141     },
36142
36143     /**
36144      * abstract method meant to be overridden by all DataWriter extensions.  It's the extension's job to apply the "data" to the "params".
36145      * The data-object provided to render is populated with data according to the meta-info defined in the user's DataReader config,
36146      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36147      * @param {Record[]} rs Store recordset
36148      * @param {Object} params Http params to be sent to server.
36149      * @param {Object} data object populated according to DataReader meta-data.
36150      */
36151     render : Ext.emptyFn,
36152
36153     /**
36154      * @cfg {Function} updateRecord Abstract method that should be implemented in all subclasses
36155      * (e.g.: {@link Ext.data.JsonWriter#updateRecord JsonWriter.updateRecord}
36156      */
36157     updateRecord : Ext.emptyFn,
36158
36159     /**
36160      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
36161      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
36162      */
36163     createRecord : Ext.emptyFn,
36164
36165     /**
36166      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
36167      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
36168      */
36169     destroyRecord : Ext.emptyFn,
36170
36171     /**
36172      * Converts a Record to a hash, taking into account the state of the Ext.data.Record along with configuration properties
36173      * related to its rendering, such as {@link #writeAllFields}, {@link Ext.data.Record#phantom phantom}, {@link Ext.data.Record#getChanges getChanges} and
36174      * {@link Ext.data.DataReader#idProperty idProperty}
36175      * @param {Ext.data.Record} rec The Record from which to create a hash.
36176      * @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.
36177      * @return {Object}
36178      * @protected
36179      * TODO Implement excludes/only configuration with 2nd param?
36180      */
36181     toHash : function(rec, config) {
36182         var map = rec.fields.map,
36183             data = {},
36184             raw = (this.writeAllFields === false && rec.phantom === false) ? rec.getChanges() : rec.data,
36185             m;
36186         Ext.iterate(raw, function(prop, value){
36187             if((m = map[prop])){
36188                 data[m.mapping ? m.mapping : m.name] = value;
36189             }
36190         });
36191         // 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.
36192         // We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
36193         // we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
36194         if (rec.phantom) {
36195             if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
36196                 delete data[this.meta.idProperty];
36197             }
36198         } else {
36199             data[this.meta.idProperty] = rec.id
36200         }
36201         return data;
36202     },
36203
36204     /**
36205      * Converts a {@link Ext.data.DataWriter#toHash Hashed} {@link Ext.data.Record} to fields-array array suitable
36206      * for encoding to xml via XTemplate, eg:
36207 <code><pre>&lt;tpl for=".">&lt;{name}>{value}&lt;/{name}&lt;/tpl></pre></code>
36208      * eg, <b>non-phantom</b>:
36209 <code><pre>{id: 1, first: 'foo', last: 'bar'} --> [{name: 'id', value: 1}, {name: 'first', value: 'foo'}, {name: 'last', value: 'bar'}]</pre></code>
36210      * {@link Ext.data.Record#phantom Phantom} records will have had their idProperty omitted in {@link #toHash} if determined to be auto-generated.
36211      * Non AUTOINCREMENT pks should have been protected.
36212      * @param {Hash} data Hashed by Ext.data.DataWriter#toHash
36213      * @return {[Object]} Array of attribute-objects.
36214      * @protected
36215      */
36216     toArray : function(data) {
36217         var fields = [];
36218         Ext.iterate(data, function(k, v) {fields.push({name: k, value: v});},this);
36219         return fields;
36220     }
36221 };/**
36222  * @class Ext.data.DataProxy
36223  * @extends Ext.util.Observable
36224  * <p>Abstract base class for implementations which provide retrieval of unformatted data objects.
36225  * This class is intended to be extended and should not be created directly. For existing implementations,
36226  * see {@link Ext.data.DirectProxy}, {@link Ext.data.HttpProxy}, {@link Ext.data.ScriptTagProxy} and
36227  * {@link Ext.data.MemoryProxy}.</p>
36228  * <p>DataProxy implementations are usually used in conjunction with an implementation of {@link Ext.data.DataReader}
36229  * (of the appropriate type which knows how to parse the data object) to provide a block of
36230  * {@link Ext.data.Records} to an {@link Ext.data.Store}.</p>
36231  * <p>The parameter to a DataProxy constructor may be an {@link Ext.data.Connection} or can also be the
36232  * config object to an {@link Ext.data.Connection}.</p>
36233  * <p>Custom implementations must implement either the <code><b>doRequest</b></code> method (preferred) or the
36234  * <code>load</code> method (deprecated). See
36235  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#doRequest doRequest} or
36236  * {@link Ext.data.HttpProxy}.{@link Ext.data.HttpProxy#load load} for additional details.</p>
36237  * <p><b><u>Example 1</u></b></p>
36238  * <pre><code>
36239 proxy: new Ext.data.ScriptTagProxy({
36240     {@link Ext.data.Connection#url url}: 'http://extjs.com/forum/topics-remote.php'
36241 }),
36242  * </code></pre>
36243  * <p><b><u>Example 2</u></b></p>
36244  * <pre><code>
36245 proxy : new Ext.data.HttpProxy({
36246     {@link Ext.data.Connection#method method}: 'GET',
36247     {@link Ext.data.HttpProxy#prettyUrls prettyUrls}: false,
36248     {@link Ext.data.Connection#url url}: 'local/default.php', // see options parameter for {@link Ext.Ajax#request}
36249     {@link #api}: {
36250         // all actions except the following will use above url
36251         create  : 'local/new.php',
36252         update  : 'local/update.php'
36253     }
36254 }),
36255  * </code></pre>
36256  * <p>And <b>new in Ext version 3</b>, attach centralized event-listeners upon the DataProxy class itself!  This is a great place
36257  * to implement a <i>messaging system</i> to centralize your application's user-feedback and error-handling.</p>
36258  * <pre><code>
36259 // Listen to all "beforewrite" event fired by all proxies.
36260 Ext.data.DataProxy.on('beforewrite', function(proxy, action) {
36261     console.log('beforewrite: ', action);
36262 });
36263
36264 // Listen to "write" event fired by all proxies
36265 Ext.data.DataProxy.on('write', function(proxy, action, data, res, rs) {
36266     console.info('write: ', action);
36267 });
36268
36269 // Listen to "exception" event fired by all proxies
36270 Ext.data.DataProxy.on('exception', function(proxy, type, action) {
36271     console.error(type + action + ' exception);
36272 });
36273  * </code></pre>
36274  * <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}.
36275  */
36276 Ext.data.DataProxy = function(conn){
36277     // make sure we have a config object here to support ux proxies.
36278     // All proxies should now send config into superclass constructor.
36279     conn = conn || {};
36280
36281     // This line caused a bug when people use custom Connection object having its own request method.
36282     // http://extjs.com/forum/showthread.php?t=67194.  Have to set DataProxy config
36283     //Ext.applyIf(this, conn);
36284
36285     this.api     = conn.api;
36286     this.url     = conn.url;
36287     this.restful = conn.restful;
36288     this.listeners = conn.listeners;
36289
36290     // deprecated
36291     this.prettyUrls = conn.prettyUrls;
36292
36293     /**
36294      * @cfg {Object} api
36295      * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".
36296      * Defaults to:<pre><code>
36297 api: {
36298     read    : undefined,
36299     create  : undefined,
36300     update  : undefined,
36301     destroy : undefined
36302 }
36303      * </code></pre>
36304      * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>
36305      * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the
36306      * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>
36307      * <p>For example:</p>
36308      * <pre><code>
36309 api: {
36310     load :    '/controller/load',
36311     create :  '/controller/new',  // Server MUST return idProperty of new record
36312     save :    '/controller/update',
36313     destroy : '/controller/destroy_action'
36314 }
36315
36316 // Alternatively, one can use the object-form to specify each API-action
36317 api: {
36318     load: {url: 'read.php', method: 'GET'},
36319     create: 'create.php',
36320     destroy: 'destroy.php',
36321     save: 'update.php'
36322 }
36323      * </code></pre>
36324      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request
36325      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>
36326      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API
36327      * property should be modified before the action is requested using the corresponding before
36328      * action event.  For example to modify the URL associated with the load action:
36329      * <pre><code>
36330 // modify the url for the action
36331 myStore.on({
36332     beforeload: {
36333         fn: function (store, options) {
36334             // use <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> to change the URL for *just* this request.
36335             store.proxy.setUrl('changed1.php');
36336
36337             // set optional second parameter to true to make this URL change
36338             // permanent, applying this URL for all subsequent requests.
36339             store.proxy.setUrl('changed1.php', true);
36340
36341             // Altering the proxy API should be done using the public
36342             // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt>.
36343             store.proxy.setApi('read', 'changed2.php');
36344
36345             // Or set the entire API with a config-object.
36346             // When using the config-object option, you must redefine the <b>entire</b>
36347             // API -- not just a specific action of it.
36348             store.proxy.setApi({
36349                 read    : 'changed_read.php',
36350                 create  : 'changed_create.php',
36351                 update  : 'changed_update.php',
36352                 destroy : 'changed_destroy.php'
36353             });
36354         }
36355     }
36356 });
36357      * </code></pre>
36358      * </p>
36359      */
36360
36361     this.addEvents(
36362         /**
36363          * @event exception
36364          * <p>Fires if an exception occurs in the Proxy during a remote request. This event is relayed
36365          * through a corresponding {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},
36366          * so any Store instance may observe this event.</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 exception events from <b>all</b>
36369          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>
36370          * <p>This event can be fired for one of two reasons:</p>
36371          * <div class="mdetail-params"><ul>
36372          * <li>remote-request <b>failed</b> : <div class="sub-desc">
36373          * The server did not return status === 200.
36374          * </div></li>
36375          * <li>remote-request <b>succeeded</b> : <div class="sub-desc">
36376          * The remote-request succeeded but the reader could not read the response.
36377          * This means the server returned data, but the configured Reader threw an
36378          * error while reading the response.  In this case, this event will be
36379          * raised and the caught error will be passed along into this event.
36380          * </div></li>
36381          * </ul></div>
36382          * <br><p>This event fires with two different contexts based upon the 2nd
36383          * parameter <tt>type [remote|response]</tt>.  The first four parameters
36384          * are identical between the two contexts -- only the final two parameters
36385          * differ.</p>
36386          * @param {DataProxy} this The proxy that sent the request
36387          * @param {String} type
36388          * <p>The value of this parameter will be either <tt>'response'</tt> or <tt>'remote'</tt>.</p>
36389          * <div class="mdetail-params"><ul>
36390          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">
36391          * <p>An <b>invalid</b> response from the server was returned: either 404,
36392          * 500 or the response meta-data does not match that defined in the DataReader
36393          * (e.g.: root, idProperty, successProperty).</p>
36394          * </div></li>
36395          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">
36396          * <p>A <b>valid</b> response was returned from the server having
36397          * successProperty === false.  This response might contain an error-message
36398          * sent from the server.  For example, the user may have failed
36399          * authentication/authorization or a database validation error occurred.</p>
36400          * </div></li>
36401          * </ul></div>
36402          * @param {String} action Name of the action (see {@link Ext.data.Api#actions}.
36403          * @param {Object} options The options for the action that were specified in the {@link #request}.
36404          * @param {Object} response
36405          * <p>The value of this parameter depends on the value of the <code>type</code> parameter:</p>
36406          * <div class="mdetail-params"><ul>
36407          * <li><b><tt>'response'</tt></b> : <div class="sub-desc">
36408          * <p>The raw browser response object (e.g.: XMLHttpRequest)</p>
36409          * </div></li>
36410          * <li><b><tt>'remote'</tt></b> : <div class="sub-desc">
36411          * <p>The decoded response object sent from the server.</p>
36412          * </div></li>
36413          * </ul></div>
36414          * @param {Mixed} arg
36415          * <p>The type and value of this parameter depends on the value of the <code>type</code> parameter:</p>
36416          * <div class="mdetail-params"><ul>
36417          * <li><b><tt>'response'</tt></b> : Error<div class="sub-desc">
36418          * <p>The JavaScript Error object caught if the configured Reader could not read the data.
36419          * If the remote request returns success===false, this parameter will be null.</p>
36420          * </div></li>
36421          * <li><b><tt>'remote'</tt></b> : Record/Record[]<div class="sub-desc">
36422          * <p>This parameter will only exist if the <tt>action</tt> was a <b>write</b> action
36423          * (Ext.data.Api.actions.create|update|destroy).</p>
36424          * </div></li>
36425          * </ul></div>
36426          */
36427         'exception',
36428         /**
36429          * @event beforeload
36430          * Fires before a request to retrieve a data object.
36431          * @param {DataProxy} this The proxy for the request
36432          * @param {Object} params The params object passed to the {@link #request} function
36433          */
36434         'beforeload',
36435         /**
36436          * @event load
36437          * Fires before the load method's callback is called.
36438          * @param {DataProxy} this The proxy for the request
36439          * @param {Object} o The request transaction object
36440          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function
36441          */
36442         'load',
36443         /**
36444          * @event loadexception
36445          * <p>This event is <b>deprecated</b>.  The signature of the loadexception event
36446          * varies depending on the proxy, use the catch-all {@link #exception} event instead.
36447          * This event will fire in addition to the {@link #exception} event.</p>
36448          * @param {misc} misc See {@link #exception}.
36449          * @deprecated
36450          */
36451         'loadexception',
36452         /**
36453          * @event beforewrite
36454          * <p>Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy</p>
36455          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
36456          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of beforewrite events from <b>all</b>
36457          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>
36458          * @param {DataProxy} this The proxy for the request
36459          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
36460          * @param {Record/Record[]} rs The Record(s) to create|update|destroy.
36461          * @param {Object} params The request <code>params</code> object.  Edit <code>params</code> to add parameters to the request.
36462          */
36463         'beforewrite',
36464         /**
36465          * @event write
36466          * <p>Fires before the request-callback is called</p>
36467          * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired
36468          * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of write events from <b>all</b>
36469          * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>
36470          * @param {DataProxy} this The proxy that sent the request
36471          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]
36472          * @param {Object} data The data object extracted from the server-response
36473          * @param {Object} response The decoded response from server
36474          * @param {Record/Record[]} rs The Record(s) from Store
36475          * @param {Object} options The callback's <tt>options</tt> property as passed to the {@link #request} function
36476          */
36477         'write'
36478     );
36479     Ext.data.DataProxy.superclass.constructor.call(this);
36480
36481     // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.
36482     try {
36483         Ext.data.Api.prepare(this);
36484     } catch (e) {
36485         if (e instanceof Ext.data.Api.Error) {
36486             e.toConsole();
36487         }
36488     }
36489     // relay each proxy's events onto Ext.data.DataProxy class for centralized Proxy-listening
36490     Ext.data.DataProxy.relayEvents(this, ['beforewrite', 'write', 'exception']);
36491 };
36492
36493 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {
36494     /**
36495      * @cfg {Boolean} restful
36496      * <p>Defaults to <tt>false</tt>.  Set to <tt>true</tt> to operate in a RESTful manner.</p>
36497      * <br><p> Note: this parameter will automatically be set to <tt>true</tt> if the
36498      * {@link Ext.data.Store} it is plugged into is set to <code>restful: true</code>. If the
36499      * Store is RESTful, there is no need to set this option on the proxy.</p>
36500      * <br><p>RESTful implementations enable the serverside framework to automatically route
36501      * actions sent to one url based upon the HTTP method, for example:
36502      * <pre><code>
36503 store: new Ext.data.Store({
36504     restful: true,
36505     proxy: new Ext.data.HttpProxy({url:'/users'}); // all requests sent to /users
36506     ...
36507 )}
36508      * </code></pre>
36509      * If there is no <code>{@link #api}</code> specified in the configuration of the proxy,
36510      * all requests will be marshalled to a single RESTful url (/users) so the serverside
36511      * framework can inspect the HTTP Method and act accordingly:
36512      * <pre>
36513 <u>Method</u>   <u>url</u>        <u>action</u>
36514 POST     /users     create
36515 GET      /users     read
36516 PUT      /users/23  update
36517 DESTROY  /users/23  delete
36518      * </pre></p>
36519      * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's
36520      * {@link Ext.data.Record#id id} will be appended to the url. Some MVC (e.g., Ruby on Rails,
36521      * Merb and Django) support segment based urls where the segments in the URL follow the
36522      * Model-View-Controller approach:<pre><code>
36523      * someSite.com/controller/action/id
36524      * </code></pre>
36525      * Where the segments in the url are typically:<div class="mdetail-params"><ul>
36526      * <li>The first segment : represents the controller class that should be invoked.</li>
36527      * <li>The second segment : represents the class function, or method, that should be called.</li>
36528      * <li>The third segment : represents the ID (a variable typically passed to the method).</li>
36529      * </ul></div></p>
36530      * <br><p>Refer to <code>{@link Ext.data.DataProxy#api}</code> for additional information.</p>
36531      */
36532     restful: false,
36533
36534     /**
36535      * <p>Redefines the Proxy's API or a single action of an API. Can be called with two method signatures.</p>
36536      * <p>If called with an object as the only parameter, the object should redefine the <b>entire</b> API, e.g.:</p><pre><code>
36537 proxy.setApi({
36538     read    : '/users/read',
36539     create  : '/users/create',
36540     update  : '/users/update',
36541     destroy : '/users/destroy'
36542 });
36543 </code></pre>
36544      * <p>If called with two parameters, the first parameter should be a string specifying the API action to
36545      * redefine and the second parameter should be the URL (or function if using DirectProxy) to call for that action, e.g.:</p><pre><code>
36546 proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');
36547 </code></pre>
36548      * @param {String/Object} api An API specification object, or the name of an action.
36549      * @param {String/Function} url The URL (or function if using DirectProxy) to call for the action.
36550      */
36551     setApi : function() {
36552         if (arguments.length == 1) {
36553             var valid = Ext.data.Api.isValid(arguments[0]);
36554             if (valid === true) {
36555                 this.api = arguments[0];
36556             }
36557             else {
36558                 throw new Ext.data.Api.Error('invalid', valid);
36559             }
36560         }
36561         else if (arguments.length == 2) {
36562             if (!Ext.data.Api.isAction(arguments[0])) {
36563                 throw new Ext.data.Api.Error('invalid', arguments[0]);
36564             }
36565             this.api[arguments[0]] = arguments[1];
36566         }
36567         Ext.data.Api.prepare(this);
36568     },
36569
36570     /**
36571      * Returns true if the specified action is defined as a unique action in the api-config.
36572      * request.  If all API-actions are routed to unique urls, the xaction parameter is unecessary.  However, if no api is defined
36573      * and all Proxy actions are routed to DataProxy#url, the server-side will require the xaction parameter to perform a switch to
36574      * the corresponding code for CRUD action.
36575      * @param {String [Ext.data.Api.CREATE|READ|UPDATE|DESTROY]} action
36576      * @return {Boolean}
36577      */
36578     isApiAction : function(action) {
36579         return (this.api[action]) ? true : false;
36580     },
36581
36582     /**
36583      * All proxy actions are executed through this method.  Automatically fires the "before" + action event
36584      * @param {String} action Name of the action
36585      * @param {Ext.data.Record/Ext.data.Record[]/null} rs Will be null when action is 'load'
36586      * @param {Object} params
36587      * @param {Ext.data.DataReader} reader
36588      * @param {Function} callback
36589      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the Proxy object.
36590      * @param {Object} options Any options specified for the action (e.g. see {@link Ext.data.Store#load}.
36591      */
36592     request : function(action, rs, params, reader, callback, scope, options) {
36593         if (!this.api[action] && !this.load) {
36594             throw new Ext.data.DataProxy.Error('action-undefined', action);
36595         }
36596         params = params || {};
36597         if ((action === Ext.data.Api.actions.read) ? this.fireEvent("beforeload", this, params) : this.fireEvent("beforewrite", this, action, rs, params) !== false) {
36598             this.doRequest.apply(this, arguments);
36599         }
36600         else {
36601             callback.call(scope || this, null, options, false);
36602         }
36603     },
36604
36605
36606     /**
36607      * <b>Deprecated</b> load method using old method signature. See {@doRequest} for preferred method.
36608      * @deprecated
36609      * @param {Object} params
36610      * @param {Object} reader
36611      * @param {Object} callback
36612      * @param {Object} scope
36613      * @param {Object} arg
36614      */
36615     load : null,
36616
36617     /**
36618      * @cfg {Function} doRequest Abstract method that should be implemented in all subclasses.  <b>Note:</b> Should only be used by custom-proxy developers.
36619      * (e.g.: {@link Ext.data.HttpProxy#doRequest HttpProxy.doRequest},
36620      * {@link Ext.data.DirectProxy#doRequest DirectProxy.doRequest}).
36621      */
36622     doRequest : function(action, rs, params, reader, callback, scope, options) {
36623         // default implementation of doRequest for backwards compatibility with 2.0 proxies.
36624         // If we're executing here, the action is probably "load".
36625         // Call with the pre-3.0 method signature.
36626         this.load(params, reader, callback, scope, options);
36627     },
36628
36629     /**
36630      * @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}.
36631      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
36632      * @param {Object} o The request transaction object
36633      * @param {Object} res The server response
36634      * @fires loadexception (deprecated)
36635      * @fires exception
36636      * @fires load
36637      * @protected
36638      */
36639     onRead : Ext.emptyFn,
36640     /**
36641      * @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}.
36642      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36643      * @param {Object} trans The request transaction object
36644      * @param {Object} res The server response
36645      * @fires exception
36646      * @fires write
36647      * @protected
36648      */
36649     onWrite : Ext.emptyFn,
36650     /**
36651      * buildUrl
36652      * Sets the appropriate url based upon the action being executed.  If restful is true, and only a single record is being acted upon,
36653      * url will be built Rails-style, as in "/controller/action/32".  restful will aply iff the supplied record is an
36654      * instance of Ext.data.Record rather than an Array of them.
36655      * @param {String} action The api action being executed [read|create|update|destroy]
36656      * @param {Ext.data.Record/Ext.data.Record[]} record The record or Array of Records being acted upon.
36657      * @return {String} url
36658      * @private
36659      */
36660     buildUrl : function(action, record) {
36661         record = record || null;
36662
36663         // conn.url gets nullified after each request.  If it's NOT null here, that means the user must have intervened with a call
36664         // to DataProxy#setUrl or DataProxy#setApi and changed it before the request was executed.  If that's the case, use conn.url,
36665         // otherwise, build the url from the api or this.url.
36666         var url = (this.conn && this.conn.url) ? this.conn.url : (this.api[action]) ? this.api[action].url : this.url;
36667         if (!url) {
36668             throw new Ext.data.Api.Error('invalid-url', action);
36669         }
36670
36671         // look for urls having "provides" suffix used in some MVC frameworks like Rails/Merb and others.  The provides suffice informs
36672         // the server what data-format the client is dealing with and returns data in the same format (eg: application/json, application/xml, etc)
36673         // e.g.: /users.json, /users.xml, etc.
36674         // with restful routes, we need urls like:
36675         // PUT /users/1.json
36676         // DELETE /users/1.json
36677         var provides = null;
36678         var m = url.match(/(.*)(\.json|\.xml|\.html)$/);
36679         if (m) {
36680             provides = m[2];    // eg ".json"
36681             url      = m[1];    // eg: "/users"
36682         }
36683         // prettyUrls is deprectated in favor of restful-config
36684         if ((this.restful === true || this.prettyUrls === true) && record instanceof Ext.data.Record && !record.phantom) {
36685             url += '/' + record.id;
36686         }
36687         return (provides === null) ? url : url + provides;
36688     },
36689
36690     /**
36691      * Destroys the proxy by purging any event listeners and cancelling any active requests.
36692      */
36693     destroy: function(){
36694         this.purgeListeners();
36695     }
36696 });
36697
36698 // Apply the Observable prototype to the DataProxy class so that proxy instances can relay their
36699 // events to the class.  Allows for centralized listening of all proxy instances upon the DataProxy class.
36700 Ext.apply(Ext.data.DataProxy, Ext.util.Observable.prototype);
36701 Ext.util.Observable.call(Ext.data.DataProxy);
36702
36703 /**
36704  * @class Ext.data.DataProxy.Error
36705  * @extends Ext.Error
36706  * DataProxy Error extension.
36707  * constructor
36708  * @param {String} message Message describing the error.
36709  * @param {Record/Record[]} arg
36710  */
36711 Ext.data.DataProxy.Error = Ext.extend(Ext.Error, {
36712     constructor : function(message, arg) {
36713         this.arg = arg;
36714         Ext.Error.call(this, message);
36715     },
36716     name: 'Ext.data.DataProxy'
36717 });
36718 Ext.apply(Ext.data.DataProxy.Error.prototype, {
36719     lang: {
36720         'action-undefined': "DataProxy attempted to execute an API-action but found an undefined url / function.  Please review your Proxy url/api-configuration.",
36721         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'
36722     }
36723 });
36724
36725
36726 /**
36727  * @class Ext.data.Request
36728  * A simple Request class used internally to the data package to provide more generalized remote-requests
36729  * to a DataProxy.
36730  * TODO Not yet implemented.  Implement in Ext.data.Store#execute
36731  */
36732 Ext.data.Request = function(params) {
36733     Ext.apply(this, params);
36734 };
36735 Ext.data.Request.prototype = {
36736     /**
36737      * @cfg {String} action
36738      */
36739     action : undefined,
36740     /**
36741      * @cfg {Ext.data.Record[]/Ext.data.Record} rs The Store recordset associated with the request.
36742      */
36743     rs : undefined,
36744     /**
36745      * @cfg {Object} params HTTP request params
36746      */
36747     params: undefined,
36748     /**
36749      * @cfg {Function} callback The function to call when request is complete
36750      */
36751     callback : Ext.emptyFn,
36752     /**
36753      * @cfg {Object} scope The scope of the callback funtion
36754      */
36755     scope : undefined,
36756     /**
36757      * @cfg {Ext.data.DataReader} reader The DataReader instance which will parse the received response
36758      */
36759     reader : undefined
36760 };
36761 /**
36762  * @class Ext.data.Response
36763  * A generic response class to normalize response-handling internally to the framework.
36764  */
36765 Ext.data.Response = function(params) {
36766     Ext.apply(this, params);
36767 };
36768 Ext.data.Response.prototype = {
36769     /**
36770      * @cfg {String} action {@link Ext.data.Api#actions}
36771      */
36772     action: undefined,
36773     /**
36774      * @cfg {Boolean} success
36775      */
36776     success : undefined,
36777     /**
36778      * @cfg {String} message
36779      */
36780     message : undefined,
36781     /**
36782      * @cfg {Array/Object} data
36783      */
36784     data: undefined,
36785     /**
36786      * @cfg {Object} raw The raw response returned from server-code
36787      */
36788     raw: undefined,
36789     /**
36790      * @cfg {Ext.data.Record/Ext.data.Record[]} records related to the Request action
36791      */
36792     records: undefined
36793 };
36794 /**
36795  * @class Ext.data.ScriptTagProxy
36796  * @extends Ext.data.DataProxy
36797  * An implementation of Ext.data.DataProxy that reads a data object from a URL which may be in a domain
36798  * other than the originating domain of the running page.<br>
36799  * <p>
36800  * <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
36801  * of the running page, you must use this class, rather than HttpProxy.</b><br>
36802  * <p>
36803  * The content passed back from a server resource requested by a ScriptTagProxy <b>must</b> be executable JavaScript
36804  * source code because it is used as the source inside a &lt;script> tag.<br>
36805  * <p>
36806  * In order for the browser to process the returned data, the server must wrap the data object
36807  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
36808  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
36809  * depending on whether the callback name was passed:
36810  * <p>
36811  * <pre><code>
36812 boolean scriptTag = false;
36813 String cb = request.getParameter("callback");
36814 if (cb != null) {
36815     scriptTag = true;
36816     response.setContentType("text/javascript");
36817 } else {
36818     response.setContentType("application/x-json");
36819 }
36820 Writer out = response.getWriter();
36821 if (scriptTag) {
36822     out.write(cb + "(");
36823 }
36824 out.print(dataBlock.toJsonString());
36825 if (scriptTag) {
36826     out.write(");");
36827 }
36828 </code></pre>
36829  * <p>Below is a PHP example to do the same thing:</p><pre><code>
36830 $callback = $_REQUEST['callback'];
36831
36832 // Create the output object.
36833 $output = array('a' => 'Apple', 'b' => 'Banana');
36834
36835 //start output
36836 if ($callback) {
36837     header('Content-Type: text/javascript');
36838     echo $callback . '(' . json_encode($output) . ');';
36839 } else {
36840     header('Content-Type: application/x-json');
36841     echo json_encode($output);
36842 }
36843 </code></pre>
36844  * <p>Below is the ASP.Net code to do the same thing:</p><pre><code>
36845 String jsonString = "{success: true}";
36846 String cb = Request.Params.Get("callback");
36847 String responseString = "";
36848 if (!String.IsNullOrEmpty(cb)) {
36849     responseString = cb + "(" + jsonString + ")";
36850 } else {
36851     responseString = jsonString;
36852 }
36853 Response.Write(responseString);
36854 </code></pre>
36855  *
36856  * @constructor
36857  * @param {Object} config A configuration object.
36858  */
36859 Ext.data.ScriptTagProxy = function(config){
36860     Ext.apply(this, config);
36861
36862     Ext.data.ScriptTagProxy.superclass.constructor.call(this, config);
36863
36864     this.head = document.getElementsByTagName("head")[0];
36865
36866     /**
36867      * @event loadexception
36868      * <b>Deprecated</b> in favor of 'exception' event.
36869      * Fires if an exception occurs in the Proxy during data loading.  This event can be fired for one of two reasons:
36870      * <ul><li><b>The load call timed out.</b>  This means the load callback did not execute within the time limit
36871      * specified by {@link #timeout}.  In this case, this event will be raised and the
36872      * fourth parameter (read error) will be null.</li>
36873      * <li><b>The load succeeded but the reader could not read the response.</b>  This means the server returned
36874      * data, but the configured Reader threw an error while reading the data.  In this case, this event will be
36875      * raised and the caught error will be passed along as the fourth parameter of this event.</li></ul>
36876      * Note that this event is also relayed through {@link Ext.data.Store}, so you can listen for it directly
36877      * on any Store instance.
36878      * @param {Object} this
36879      * @param {Object} options The loading options that were specified (see {@link #load} for details).  If the load
36880      * call timed out, this parameter will be null.
36881      * @param {Object} arg The callback's arg object passed to the {@link #load} function
36882      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data.
36883      * If the remote request returns success: false, this parameter will be null.
36884      */
36885 };
36886
36887 Ext.data.ScriptTagProxy.TRANS_ID = 1000;
36888
36889 Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {
36890     /**
36891      * @cfg {String} url The URL from which to request the data object.
36892      */
36893     /**
36894      * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
36895      */
36896     timeout : 30000,
36897     /**
36898      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
36899      * the server the name of the callback function set up by the load call to process the returned data object.
36900      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
36901      * javascript output which calls this named function passing the data object as its only parameter.
36902      */
36903     callbackParam : "callback",
36904     /**
36905      *  @cfg {Boolean} nocache (optional) Defaults to true. Disable caching by adding a unique parameter
36906      * name to the request.
36907      */
36908     nocache : true,
36909
36910     /**
36911      * HttpProxy implementation of DataProxy#doRequest
36912      * @param {String} action
36913      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is <tt>read</tt>, rs will be null
36914      * @param {Object} params An object containing properties which are to be used as HTTP parameters
36915      * for the request to the remote server.
36916      * @param {Ext.data.DataReader} reader The Reader object which converts the data
36917      * object into a block of Ext.data.Records.
36918      * @param {Function} callback The function into which to pass the block of Ext.data.Records.
36919      * The function must be passed <ul>
36920      * <li>The Record block object</li>
36921      * <li>The "arg" argument from the load function</li>
36922      * <li>A boolean success indicator</li>
36923      * </ul>
36924      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
36925      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
36926      */
36927     doRequest : function(action, rs, params, reader, callback, scope, arg) {
36928         var p = Ext.urlEncode(Ext.apply(params, this.extraParams));
36929
36930         var url = this.buildUrl(action, rs);
36931         if (!url) {
36932             throw new Ext.data.Api.Error('invalid-url', url);
36933         }
36934         url = Ext.urlAppend(url, p);
36935
36936         if(this.nocache){
36937             url = Ext.urlAppend(url, '_dc=' + (new Date().getTime()));
36938         }
36939         var transId = ++Ext.data.ScriptTagProxy.TRANS_ID;
36940         var trans = {
36941             id : transId,
36942             action: action,
36943             cb : "stcCallback"+transId,
36944             scriptId : "stcScript"+transId,
36945             params : params,
36946             arg : arg,
36947             url : url,
36948             callback : callback,
36949             scope : scope,
36950             reader : reader
36951         };
36952         window[trans.cb] = this.createCallback(action, rs, trans);
36953         url += String.format("&{0}={1}", this.callbackParam, trans.cb);
36954         if(this.autoAbort !== false){
36955             this.abort();
36956         }
36957
36958         trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
36959
36960         var script = document.createElement("script");
36961         script.setAttribute("src", url);
36962         script.setAttribute("type", "text/javascript");
36963         script.setAttribute("id", trans.scriptId);
36964         this.head.appendChild(script);
36965
36966         this.trans = trans;
36967     },
36968
36969     // @private createCallback
36970     createCallback : function(action, rs, trans) {
36971         var self = this;
36972         return function(res) {
36973             self.trans = false;
36974             self.destroyTrans(trans, true);
36975             if (action === Ext.data.Api.actions.read) {
36976                 self.onRead.call(self, action, trans, res);
36977             } else {
36978                 self.onWrite.call(self, action, trans, res, rs);
36979             }
36980         };
36981     },
36982     /**
36983      * Callback for read actions
36984      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
36985      * @param {Object} trans The request transaction object
36986      * @param {Object} res The server response
36987      * @protected
36988      */
36989     onRead : function(action, trans, res) {
36990         var result;
36991         try {
36992             result = trans.reader.readRecords(res);
36993         }catch(e){
36994             // @deprecated: fire loadexception
36995             this.fireEvent("loadexception", this, trans, res, e);
36996
36997             this.fireEvent('exception', this, 'response', action, trans, res, e);
36998             trans.callback.call(trans.scope||window, null, trans.arg, false);
36999             return;
37000         }
37001         if (result.success === false) {
37002             // @deprecated: fire old loadexception for backwards-compat.
37003             this.fireEvent('loadexception', this, trans, res);
37004
37005             this.fireEvent('exception', this, 'remote', action, trans, res, null);
37006         } else {
37007             this.fireEvent("load", this, res, trans.arg);
37008         }
37009         trans.callback.call(trans.scope||window, result, trans.arg, result.success);
37010     },
37011     /**
37012      * Callback for write actions
37013      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
37014      * @param {Object} trans The request transaction object
37015      * @param {Object} res The server response
37016      * @protected
37017      */
37018     onWrite : function(action, trans, response, rs) {
37019         var reader = trans.reader;
37020         try {
37021             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.
37022             var res = reader.readResponse(action, response);
37023         } catch (e) {
37024             this.fireEvent('exception', this, 'response', action, trans, res, e);
37025             trans.callback.call(trans.scope||window, null, res, false);
37026             return;
37027         }
37028         if(!res.success === true){
37029             this.fireEvent('exception', this, 'remote', action, trans, res, rs);
37030             trans.callback.call(trans.scope||window, null, res, false);
37031             return;
37032         }
37033         this.fireEvent("write", this, action, res.data, res, rs, trans.arg );
37034         trans.callback.call(trans.scope||window, res.data, res, true);
37035     },
37036
37037     // private
37038     isLoading : function(){
37039         return this.trans ? true : false;
37040     },
37041
37042     /**
37043      * Abort the current server request.
37044      */
37045     abort : function(){
37046         if(this.isLoading()){
37047             this.destroyTrans(this.trans);
37048         }
37049     },
37050
37051     // private
37052     destroyTrans : function(trans, isLoaded){
37053         this.head.removeChild(document.getElementById(trans.scriptId));
37054         clearTimeout(trans.timeoutId);
37055         if(isLoaded){
37056             window[trans.cb] = undefined;
37057             try{
37058                 delete window[trans.cb];
37059             }catch(e){}
37060         }else{
37061             // if hasn't been loaded, wait for load to remove it to prevent script error
37062             window[trans.cb] = function(){
37063                 window[trans.cb] = undefined;
37064                 try{
37065                     delete window[trans.cb];
37066                 }catch(e){}
37067             };
37068         }
37069     },
37070
37071     // private
37072     handleFailure : function(trans){
37073         this.trans = false;
37074         this.destroyTrans(trans, false);
37075         if (trans.action === Ext.data.Api.actions.read) {
37076             // @deprecated firing loadexception
37077             this.fireEvent("loadexception", this, null, trans.arg);
37078         }
37079
37080         this.fireEvent('exception', this, 'response', trans.action, {
37081             response: null,
37082             options: trans.arg
37083         });
37084         trans.callback.call(trans.scope||window, null, trans.arg, false);
37085     },
37086
37087     // inherit docs
37088     destroy: function(){
37089         this.abort();
37090         Ext.data.ScriptTagProxy.superclass.destroy.call(this);
37091     }
37092 });/**
37093  * @class Ext.data.HttpProxy
37094  * @extends Ext.data.DataProxy
37095  * <p>An implementation of {@link Ext.data.DataProxy} that processes data requests within the same
37096  * domain of the originating page.</p>
37097  * <p><b>Note</b>: this class cannot be used to retrieve data from a domain other
37098  * than the domain from which the running page was served. For cross-domain requests, use a
37099  * {@link Ext.data.ScriptTagProxy ScriptTagProxy}.</p>
37100  * <p>Be aware that to enable the browser to parse an XML document, the server must set
37101  * the Content-Type header in the HTTP response to "<tt>text/xml</tt>".</p>
37102  * @constructor
37103  * @param {Object} conn
37104  * An {@link Ext.data.Connection} object, or options parameter to {@link Ext.Ajax#request}.
37105  * <p>Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the
37106  * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>
37107  * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,
37108  * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be
37109  * used to pass parameters known at instantiation time.</p>
37110  * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make
37111  * the request.</p>
37112  */
37113 Ext.data.HttpProxy = function(conn){
37114     Ext.data.HttpProxy.superclass.constructor.call(this, conn);
37115
37116     /**
37117      * The Connection object (Or options parameter to {@link Ext.Ajax#request}) which this HttpProxy
37118      * uses to make requests to the server. Properties of this object may be changed dynamically to
37119      * change the way data is requested.
37120      * @property
37121      */
37122     this.conn = conn;
37123
37124     // nullify the connection url.  The url param has been copied to 'this' above.  The connection
37125     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the
37126     // connection url during beforeaction events (ie: beforeload, beforewrite, etc).
37127     // Url is always re-defined during doRequest.
37128     this.conn.url = null;
37129
37130     this.useAjax = !conn || !conn.events;
37131
37132     // A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]
37133     var actions = Ext.data.Api.actions;
37134     this.activeRequest = {};
37135     for (var verb in actions) {
37136         this.activeRequest[actions[verb]] = undefined;
37137     }
37138 };
37139
37140 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {
37141     /**
37142      * Return the {@link Ext.data.Connection} object being used by this Proxy.
37143      * @return {Connection} The Connection object. This object may be used to subscribe to events on
37144      * a finer-grained basis than the DataProxy events.
37145      */
37146     getConnection : function() {
37147         return this.useAjax ? Ext.Ajax : this.conn;
37148     },
37149
37150     /**
37151      * Used for overriding the url used for a single request.  Designed to be called during a beforeaction event.  Calling setUrl
37152      * will override any urls set via the api configuration parameter.  Set the optional parameter makePermanent to set the url for
37153      * all subsequent requests.  If not set to makePermanent, the next request will use the same url or api configuration defined
37154      * in the initial proxy configuration.
37155      * @param {String} url
37156      * @param {Boolean} makePermanent (Optional) [false]
37157      *
37158      * (e.g.: beforeload, beforesave, etc).
37159      */
37160     setUrl : function(url, makePermanent) {
37161         this.conn.url = url;
37162         if (makePermanent === true) {
37163             this.url = url;
37164             this.api = null;
37165             Ext.data.Api.prepare(this);
37166         }
37167     },
37168
37169     /**
37170      * HttpProxy implementation of DataProxy#doRequest
37171      * @param {String} action The crud action type (create, read, update, destroy)
37172      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
37173      * @param {Object} params An object containing properties which are to be used as HTTP parameters
37174      * for the request to the remote server.
37175      * @param {Ext.data.DataReader} reader The Reader object which converts the data
37176      * object into a block of Ext.data.Records.
37177      * @param {Function} callback
37178      * <div class="sub-desc"><p>A function to be called after the request.
37179      * The <tt>callback</tt> is passed the following arguments:<ul>
37180      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
37181      * <li><tt>options</tt>: Options object from the action request</li>
37182      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
37183      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
37184      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
37185      * @protected
37186      */
37187     doRequest : function(action, rs, params, reader, cb, scope, arg) {
37188         var  o = {
37189             method: (this.api[action]) ? this.api[action]['method'] : undefined,
37190             request: {
37191                 callback : cb,
37192                 scope : scope,
37193                 arg : arg
37194             },
37195             reader: reader,
37196             callback : this.createCallback(action, rs),
37197             scope: this
37198         };
37199
37200         // If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).
37201         // Use std HTTP params otherwise.
37202         if (params.jsonData) {
37203             o.jsonData = params.jsonData;
37204         } else if (params.xmlData) {
37205             o.xmlData = params.xmlData;
37206         } else {
37207             o.params = params || {};
37208         }
37209         // Set the connection url.  If this.conn.url is not null here,
37210         // the user must have overridden the url during a beforewrite/beforeload event-handler.
37211         // this.conn.url is nullified after each request.
37212         this.conn.url = this.buildUrl(action, rs);
37213
37214         if(this.useAjax){
37215
37216             Ext.applyIf(o, this.conn);
37217
37218             // If a currently running request is found for this action, abort it.
37219             if (this.activeRequest[action]) {
37220                 ////
37221                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array
37222                 // TODO ideas anyone?
37223                 //
37224                 //Ext.Ajax.abort(this.activeRequest[action]);
37225             }
37226             this.activeRequest[action] = Ext.Ajax.request(o);
37227         }else{
37228             this.conn.request(o);
37229         }
37230         // request is sent, nullify the connection url in preparation for the next request
37231         this.conn.url = null;
37232     },
37233
37234     /**
37235      * Returns a callback function for a request.  Note a special case is made for the
37236      * read action vs all the others.
37237      * @param {String} action [create|update|delete|load]
37238      * @param {Ext.data.Record[]} rs The Store-recordset being acted upon
37239      * @private
37240      */
37241     createCallback : function(action, rs) {
37242         return function(o, success, response) {
37243             this.activeRequest[action] = undefined;
37244             if (!success) {
37245                 if (action === Ext.data.Api.actions.read) {
37246                     // @deprecated: fire loadexception for backwards compat.
37247                     // TODO remove
37248                     this.fireEvent('loadexception', this, o, response);
37249                 }
37250                 this.fireEvent('exception', this, 'response', action, o, response);
37251                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37252                 return;
37253             }
37254             if (action === Ext.data.Api.actions.read) {
37255                 this.onRead(action, o, response);
37256             } else {
37257                 this.onWrite(action, o, response, rs);
37258             }
37259         };
37260     },
37261
37262     /**
37263      * Callback for read action
37264      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
37265      * @param {Object} o The request transaction object
37266      * @param {Object} res The server response
37267      * @fires loadexception (deprecated)
37268      * @fires exception
37269      * @fires load
37270      * @protected
37271      */
37272     onRead : function(action, o, response) {
37273         var result;
37274         try {
37275             result = o.reader.read(response);
37276         }catch(e){
37277             // @deprecated: fire old loadexception for backwards-compat.
37278             // TODO remove
37279             this.fireEvent('loadexception', this, o, response, e);
37280
37281             this.fireEvent('exception', this, 'response', action, o, response, e);
37282             o.request.callback.call(o.request.scope, null, o.request.arg, false);
37283             return;
37284         }
37285         if (result.success === false) {
37286             // @deprecated: fire old loadexception for backwards-compat.
37287             // TODO remove
37288             this.fireEvent('loadexception', this, o, response);
37289
37290             // Get DataReader read-back a response-object to pass along to exception event
37291             var res = o.reader.readResponse(action, response);
37292             this.fireEvent('exception', this, 'remote', action, o, res, null);
37293         }
37294         else {
37295             this.fireEvent('load', this, o, o.request.arg);
37296         }
37297         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
37298         // the calls to request.callback(...) in each will have to be made identical.
37299         // NOTE reader.readResponse does not currently return Ext.data.Response
37300         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);
37301     },
37302     /**
37303      * Callback for write actions
37304      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
37305      * @param {Object} trans The request transaction object
37306      * @param {Object} res The server response
37307      * @fires exception
37308      * @fires write
37309      * @protected
37310      */
37311     onWrite : function(action, o, response, rs) {
37312         var reader = o.reader;
37313         var res;
37314         try {
37315             res = reader.readResponse(action, response);
37316         } catch (e) {
37317             this.fireEvent('exception', this, 'response', action, o, response, e);
37318             o.request.callback.call(o.request.scope, null, o.request.arg, false);
37319             return;
37320         }
37321         if (res.success === true) {
37322             this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);
37323         } else {
37324             this.fireEvent('exception', this, 'remote', action, o, res, rs);
37325         }
37326         // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
37327         // the calls to request.callback(...) in each will have to be made similar.
37328         // NOTE reader.readResponse does not currently return Ext.data.Response
37329         o.request.callback.call(o.request.scope, res.data, res, res.success);
37330     },
37331
37332     // inherit docs
37333     destroy: function(){
37334         if(!this.useAjax){
37335             this.conn.abort();
37336         }else if(this.activeRequest){
37337             var actions = Ext.data.Api.actions;
37338             for (var verb in actions) {
37339                 if(this.activeRequest[actions[verb]]){
37340                     Ext.Ajax.abort(this.activeRequest[actions[verb]]);
37341                 }
37342             }
37343         }
37344         Ext.data.HttpProxy.superclass.destroy.call(this);
37345     }
37346 });/**
37347  * @class Ext.data.MemoryProxy
37348  * @extends Ext.data.DataProxy
37349  * An implementation of Ext.data.DataProxy that simply passes the data specified in its constructor
37350  * to the Reader when its load method is called.
37351  * @constructor
37352  * @param {Object} data The data object which the Reader uses to construct a block of Ext.data.Records.
37353  */
37354 Ext.data.MemoryProxy = function(data){
37355     // Must define a dummy api with "read" action to satisfy DataProxy#doRequest and Ext.data.Api#prepare *before* calling super
37356     var api = {};
37357     api[Ext.data.Api.actions.read] = true;
37358     Ext.data.MemoryProxy.superclass.constructor.call(this, {
37359         api: api
37360     });
37361     this.data = data;
37362 };
37363
37364 Ext.extend(Ext.data.MemoryProxy, Ext.data.DataProxy, {
37365     /**
37366      * @event loadexception
37367      * Fires if an exception occurs in the Proxy during data loading. Note that this event is also relayed
37368      * through {@link Ext.data.Store}, so you can listen for it directly on any Store instance.
37369      * @param {Object} this
37370      * @param {Object} arg The callback's arg object passed to the {@link #load} function
37371      * @param {Object} null This parameter does not apply and will always be null for MemoryProxy
37372      * @param {Error} e The JavaScript Error object caught if the configured Reader could not read the data
37373      */
37374
37375        /**
37376      * MemoryProxy implementation of DataProxy#doRequest
37377      * @param {String} action
37378      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
37379      * @param {Object} params An object containing properties which are to be used as HTTP parameters
37380      * for the request to the remote server.
37381      * @param {Ext.data.DataReader} reader The Reader object which converts the data
37382      * object into a block of Ext.data.Records.
37383      * @param {Function} callback The function into which to pass the block of Ext.data.Records.
37384      * The function must be passed <ul>
37385      * <li>The Record block object</li>
37386      * <li>The "arg" argument from the load function</li>
37387      * <li>A boolean success indicator</li>
37388      * </ul>
37389      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
37390      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
37391      */
37392     doRequest : function(action, rs, params, reader, callback, scope, arg) {
37393         // No implementation for CRUD in MemoryProxy.  Assumes all actions are 'load'
37394         params = params || {};
37395         var result;
37396         try {
37397             result = reader.readRecords(this.data);
37398         }catch(e){
37399             // @deprecated loadexception
37400             this.fireEvent("loadexception", this, null, arg, e);
37401
37402             this.fireEvent('exception', this, 'response', action, arg, null, e);
37403             callback.call(scope, null, arg, false);
37404             return;
37405         }
37406         callback.call(scope, result, arg, true);
37407     }
37408 });/**
37409  * @class Ext.data.Types
37410  * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
37411  * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
37412  * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
37413  * of this class.</p>
37414  * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
37415  * each type definition must contain three properties:</p>
37416  * <div class="mdetail-params"><ul>
37417  * <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
37418  * to be stored in the Field. The function is passed the collowing parameters:
37419  * <div class="mdetail-params"><ul>
37420  * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
37421  * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
37422  * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
37423  * Depending on the Reader type, this could be an Array ({@link Ext.data.ArrayReader ArrayReader}), an object
37424  * ({@link Ext.data.JsonReader JsonReader}), or an XML element ({@link Ext.data.XMLReader XMLReader}).</div></li>
37425  * </ul></div></div></li>
37426  * <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>
37427  * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
37428  * </ul></div>
37429  * <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
37430  * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
37431  *<pre><code>
37432 // Add a new Field data type which stores a VELatLong object in the Record.
37433 Ext.data.Types.VELATLONG = {
37434     convert: function(v, data) {
37435         return new VELatLong(data.lat, data.long);
37436     },
37437     sortType: function(v) {
37438         return v.Latitude;  // When sorting, order by latitude
37439     },
37440     type: 'VELatLong'
37441 };
37442 </code></pre>
37443  * <p>Then, when declaring a Record, use <pre><code>
37444 var types = Ext.data.Types; // allow shorthand type access
37445 UnitRecord = Ext.data.Record.create([
37446     { name: 'unitName', mapping: 'UnitName' },
37447     { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
37448     { name: 'latitude', mapping: 'lat', type: types.FLOAT },
37449     { name: 'latitude', mapping: 'lat', type: types.FLOAT },
37450     { name: 'position', type: types.VELATLONG }
37451 ]);
37452 </code></pre>
37453  * @singleton
37454  */
37455 Ext.data.Types = new function(){
37456     var st = Ext.data.SortTypes;
37457     Ext.apply(this, {
37458         /**
37459          * @type Regexp
37460          * @property stripRe
37461          * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
37462          * This should be overridden for localization.
37463          */
37464         stripRe: /[\$,%]/g,
37465         
37466         /**
37467          * @type Object.
37468          * @property AUTO
37469          * This data type means that no conversion is applied to the raw data before it is placed into a Record.
37470          */
37471         AUTO: {
37472             convert: function(v){ return v; },
37473             sortType: st.none,
37474             type: 'auto'
37475         },
37476
37477         /**
37478          * @type Object.
37479          * @property STRING
37480          * This data type means that the raw data is converted into a String before it is placed into a Record.
37481          */
37482         STRING: {
37483             convert: function(v){ return (v === undefined || v === null) ? '' : String(v); },
37484             sortType: st.asUCString,
37485             type: 'string'
37486         },
37487
37488         /**
37489          * @type Object.
37490          * @property INT
37491          * This data type means that the raw data is converted into an integer before it is placed into a Record.
37492          * <p>The synonym <code>INTEGER</code> is equivalent.</p>
37493          */
37494         INT: {
37495             convert: function(v){
37496                 return v !== undefined && v !== null && v !== '' ?
37497                     parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : 0;
37498             },
37499             sortType: st.none,
37500             type: 'int'
37501         },
37502         
37503         /**
37504          * @type Object.
37505          * @property FLOAT
37506          * This data type means that the raw data is converted into a number before it is placed into a Record.
37507          * <p>The synonym <code>NUMBER</code> is equivalent.</p>
37508          */
37509         FLOAT: {
37510             convert: function(v){
37511                 return v !== undefined && v !== null && v !== '' ?
37512                     parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : 0;
37513             },
37514             sortType: st.none,
37515             type: 'float'
37516         },
37517         
37518         /**
37519          * @type Object.
37520          * @property BOOL
37521          * <p>This data type means that the raw data is converted into a boolean before it is placed into
37522          * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
37523          * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
37524          */
37525         BOOL: {
37526             convert: function(v){ return v === true || v === 'true' || v == 1; },
37527             sortType: st.none,
37528             type: 'bool'
37529         },
37530         
37531         /**
37532          * @type Object.
37533          * @property DATE
37534          * This data type means that the raw data is converted into a Date before it is placed into a Record.
37535          * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
37536          * being applied.
37537          */
37538         DATE: {
37539             convert: function(v){
37540                 var df = this.dateFormat;
37541                 if(!v){
37542                     return null;
37543                 }
37544                 if(Ext.isDate(v)){
37545                     return v;
37546                 }
37547                 if(df){
37548                     if(df == 'timestamp'){
37549                         return new Date(v*1000);
37550                     }
37551                     if(df == 'time'){
37552                         return new Date(parseInt(v, 10));
37553                     }
37554                     return Date.parseDate(v, df);
37555                 }
37556                 var parsed = Date.parse(v);
37557                 return parsed ? new Date(parsed) : null;
37558             },
37559             sortType: st.asDate,
37560             type: 'date'
37561         }
37562     });
37563     
37564     Ext.apply(this, {
37565         /**
37566          * @type Object.
37567          * @property BOOLEAN
37568          * <p>This data type means that the raw data is converted into a boolean before it is placed into
37569          * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
37570          * <p>The synonym <code>BOOL</code> is equivalent.</p>
37571          */
37572         BOOLEAN: this.BOOL,
37573         /**
37574          * @type Object.
37575          * @property INTEGER
37576          * This data type means that the raw data is converted into an integer before it is placed into a Record.
37577          * <p>The synonym <code>INT</code> is equivalent.</p>
37578          */
37579         INTEGER: this.INT,
37580         /**
37581          * @type Object.
37582          * @property NUMBER
37583          * This data type means that the raw data is converted into a number before it is placed into a Record.
37584          * <p>The synonym <code>FLOAT</code> is equivalent.</p>
37585          */
37586         NUMBER: this.FLOAT    
37587     });
37588 };/**
37589  * @class Ext.data.JsonWriter
37590  * @extends Ext.data.DataWriter
37591  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action.
37592  */
37593 Ext.data.JsonWriter = Ext.extend(Ext.data.DataWriter, {
37594     /**
37595      * @cfg {Boolean} encode <tt>true</tt> to {@link Ext.util.JSON#encode encode} the
37596      * {@link Ext.data.DataWriter#toHash hashed data}. Defaults to <tt>true</tt>.  When using
37597      * {@link Ext.data.DirectProxy}, set this to <tt>false</tt> since Ext.Direct.JsonProvider will perform
37598      * its own json-encoding.  In addition, if you're using {@link Ext.data.HttpProxy}, setting to <tt>false</tt>
37599      * will cause HttpProxy to transmit data using the <b>jsonData</b> configuration-params of {@link Ext.Ajax#request}
37600      * instead of <b>params</b>.  When using a {@link Ext.data.Store#restful} Store, some serverside frameworks are
37601      * tuned to expect data through the jsonData mechanism.  In those cases, one will want to set <b>encode: <tt>false</tt></b>, as in
37602      * let the lower-level connection object (eg: Ext.Ajax) do the encoding.
37603      */
37604     encode : true,
37605     /**
37606      * @cfg {Boolean} encodeDelete False to send only the id to the server on delete, true to encode it in an object
37607      * literal, eg: <pre><code>
37608 {id: 1}
37609  * </code></pre> Defaults to <tt>false</tt>
37610      */
37611     encodeDelete: false,
37612     
37613     constructor : function(config){
37614         Ext.data.JsonWriter.superclass.constructor.call(this, config);    
37615     },
37616
37617     /**
37618      * Final action of a write event.  Apply the written data-object to params.
37619      * @param {Object} http params-object to write-to.
37620      * @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}.
37621      * @param {Object/Object[]} data Data-object representing compiled Store-recordset.
37622      */
37623     render : function(params, baseParams, data) {
37624         if (this.encode === true) {
37625             // Encode here now.
37626             Ext.apply(params, baseParams);
37627             params[this.meta.root] = Ext.encode(data);
37628         } else {
37629             // defer encoding for some other layer, probably in {@link Ext.Ajax#request}.  Place everything into "jsonData" key.
37630             var jdata = Ext.apply({}, baseParams);
37631             jdata[this.meta.root] = data;
37632             params.jsonData = jdata;
37633         }
37634     },
37635     /**
37636      * Implements abstract Ext.data.DataWriter#createRecord
37637      * @protected
37638      * @param {Ext.data.Record} rec
37639      * @return {Object}
37640      */
37641     createRecord : function(rec) {
37642        return this.toHash(rec);
37643     },
37644     /**
37645      * Implements abstract Ext.data.DataWriter#updateRecord
37646      * @protected
37647      * @param {Ext.data.Record} rec
37648      * @return {Object}
37649      */
37650     updateRecord : function(rec) {
37651         return this.toHash(rec);
37652
37653     },
37654     /**
37655      * Implements abstract Ext.data.DataWriter#destroyRecord
37656      * @protected
37657      * @param {Ext.data.Record} rec
37658      * @return {Object}
37659      */
37660     destroyRecord : function(rec){
37661         if(this.encodeDelete){
37662             var data = {};
37663             data[this.meta.idProperty] = rec.id;
37664             return data;
37665         }else{
37666             return rec.id;
37667         }
37668     }
37669 });/**
37670  * @class Ext.data.JsonReader
37671  * @extends Ext.data.DataReader
37672  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects
37673  * from a JSON packet based on mappings in a provided {@link Ext.data.Record}
37674  * constructor.</p>
37675  * <p>Example code:</p>
37676  * <pre><code>
37677 var myReader = new Ext.data.JsonReader({
37678     // metadata configuration options:
37679     {@link #idProperty}: 'id'
37680     {@link #root}: 'rows',
37681     {@link #totalProperty}: 'results',
37682     {@link Ext.data.DataReader#messageProperty}: "msg"  // The element within the response that provides a user-feedback message (optional)
37683
37684     // the fields config option will internally create an {@link Ext.data.Record}
37685     // constructor that provides mapping for reading the record data objects
37686     {@link Ext.data.DataReader#fields fields}: [
37687         // map Record&#39;s 'firstname' field to data object&#39;s key of same name
37688         {name: 'name'},
37689         // map Record&#39;s 'job' field to data object&#39;s 'occupation' key
37690         {name: 'job', mapping: 'occupation'}
37691     ]
37692 });
37693 </code></pre>
37694  * <p>This would consume a JSON data object of the form:</p><pre><code>
37695 {
37696     results: 2000, // Reader&#39;s configured {@link #totalProperty}
37697     rows: [        // Reader&#39;s configured {@link #root}
37698         // record data objects:
37699         { {@link #idProperty id}: 1, firstname: 'Bill', occupation: 'Gardener' },
37700         { {@link #idProperty id}: 2, firstname: 'Ben' , occupation: 'Horticulturalist' },
37701         ...
37702     ]
37703 }
37704 </code></pre>
37705  * <p><b><u>Automatic configuration using metaData</u></b></p>
37706  * <p>It is possible to change a JsonReader's metadata at any time by including
37707  * a <b><tt>metaData</tt></b> property in the JSON data object. If the JSON data
37708  * object has a <b><tt>metaData</tt></b> property, a {@link Ext.data.Store Store}
37709  * object using this Reader will reconfigure itself to use the newly provided
37710  * field definition and fire its {@link Ext.data.Store#metachange metachange}
37711  * event. The metachange event handler may interrogate the <b><tt>metaData</tt></b>
37712  * property to perform any configuration required.</p>
37713  * <p>Note that reconfiguring a Store potentially invalidates objects which may
37714  * refer to Fields or Records which no longer exist.</p>
37715  * <p>To use this facility you would create the JsonReader like this:</p><pre><code>
37716 var myReader = new Ext.data.JsonReader();
37717 </code></pre>
37718  * <p>The first data packet from the server would configure the reader by
37719  * containing a <b><tt>metaData</tt></b> property <b>and</b> the data. For
37720  * example, the JSON data object might take the form:</p><pre><code>
37721 {
37722     metaData: {
37723         "{@link #idProperty}": "id",
37724         "{@link #root}": "rows",
37725         "{@link #totalProperty}": "results"
37726         "{@link #successProperty}": "success",
37727         "{@link Ext.data.DataReader#fields fields}": [
37728             {"name": "name"},
37729             {"name": "job", "mapping": "occupation"}
37730         ],
37731         // used by store to set its sortInfo
37732         "sortInfo":{
37733            "field": "name",
37734            "direction": "ASC"
37735         },
37736         // {@link Ext.PagingToolbar paging data} (if applicable)
37737         "start": 0,
37738         "limit": 2,
37739         // custom property
37740         "foo": "bar"
37741     },
37742     // Reader&#39;s configured {@link #successProperty}
37743     "success": true,
37744     // Reader&#39;s configured {@link #totalProperty}
37745     "results": 2000,
37746     // Reader&#39;s configured {@link #root}
37747     // (this data simulates 2 results {@link Ext.PagingToolbar per page})
37748     "rows": [ // <b>*Note:</b> this must be an Array
37749         { "id": 1, "name": "Bill", "occupation": "Gardener" },
37750         { "id": 2, "name":  "Ben", "occupation": "Horticulturalist" }
37751     ]
37752 }
37753  * </code></pre>
37754  * <p>The <b><tt>metaData</tt></b> property in the JSON data object should contain:</p>
37755  * <div class="mdetail-params"><ul>
37756  * <li>any of the configuration options for this class</li>
37757  * <li>a <b><tt>{@link Ext.data.Record#fields fields}</tt></b> property which
37758  * the JsonReader will use as an argument to the
37759  * {@link Ext.data.Record#create data Record create method} in order to
37760  * configure the layout of the Records it will produce.</li>
37761  * <li>a <b><tt>{@link Ext.data.Store#sortInfo sortInfo}</tt></b> property
37762  * which the JsonReader will use to set the {@link Ext.data.Store}'s
37763  * {@link Ext.data.Store#sortInfo sortInfo} property</li>
37764  * <li>any custom properties needed</li>
37765  * </ul></div>
37766  *
37767  * @constructor
37768  * Create a new JsonReader
37769  * @param {Object} meta Metadata configuration options.
37770  * @param {Array/Object} recordType
37771  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
37772  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
37773  * constructor created from {@link Ext.data.Record#create}.</p>
37774  */
37775 Ext.data.JsonReader = function(meta, recordType){
37776     meta = meta || {};
37777     /**
37778      * @cfg {String} idProperty [id] Name of the property within a row object
37779      * that contains a record identifier value.  Defaults to <tt>id</tt>
37780      */
37781     /**
37782      * @cfg {String} successProperty [success] Name of the property from which to
37783      * retrieve the success attribute. Defaults to <tt>success</tt>.  See
37784      * {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
37785      * for additional information.
37786      */
37787     /**
37788      * @cfg {String} totalProperty [total] Name of the property from which to
37789      * retrieve the total number of records in the dataset. This is only needed
37790      * if the whole dataset is not passed in one go, but is being paged from
37791      * the remote server.  Defaults to <tt>total</tt>.
37792      */
37793     /**
37794      * @cfg {String} root [undefined] <b>Required</b>.  The name of the property
37795      * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
37796      * An exception will be thrown if the root property is undefined. The data
37797      * packet value for this property should be an empty array to clear the data
37798      * or show no data.
37799      */
37800     Ext.applyIf(meta, {
37801         idProperty: 'id',
37802         successProperty: 'success',
37803         totalProperty: 'total'
37804     });
37805
37806     Ext.data.JsonReader.superclass.constructor.call(this, meta, recordType || meta.fields);
37807 };
37808 Ext.extend(Ext.data.JsonReader, Ext.data.DataReader, {
37809     /**
37810      * This JsonReader's metadata as passed to the constructor, or as passed in
37811      * the last data packet's <b><tt>metaData</tt></b> property.
37812      * @type Mixed
37813      * @property meta
37814      */
37815     /**
37816      * This method is only used by a DataProxy which has retrieved data from a remote server.
37817      * @param {Object} response The XHR object which contains the JSON data in its responseText.
37818      * @return {Object} data A data block which is used by an Ext.data.Store object as
37819      * a cache of Ext.data.Records.
37820      */
37821     read : function(response){
37822         var json = response.responseText;
37823         var o = Ext.decode(json);
37824         if(!o) {
37825             throw {message: 'JsonReader.read: Json object not found'};
37826         }
37827         return this.readRecords(o);
37828     },
37829
37830     /*
37831      * TODO: refactor code between JsonReader#readRecords, #readResponse into 1 method.
37832      * there's ugly duplication going on due to maintaining backwards compat. with 2.0.  It's time to do this.
37833      */
37834     /**
37835      * Decode a JSON response from server.
37836      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
37837      * @param {Object} response The XHR object returned through an Ajax server request.
37838      * @return {Response} A {@link Ext.data.Response Response} object containing the data response, and also status information.
37839      */
37840     readResponse : function(action, response) {
37841         var o = (response.responseText !== undefined) ? Ext.decode(response.responseText) : response;
37842         if(!o) {
37843             throw new Ext.data.JsonReader.Error('response');
37844         }
37845
37846         var root = this.getRoot(o);
37847         if (action === Ext.data.Api.actions.create) {
37848             var def = Ext.isDefined(root);
37849             if (def && Ext.isEmpty(root)) {
37850                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
37851             }
37852             else if (!def) {
37853                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
37854             }
37855         }
37856
37857         // instantiate response object
37858         var res = new Ext.data.Response({
37859             action: action,
37860             success: this.getSuccess(o),
37861             data: (root) ? this.extractData(root, false) : [],
37862             message: this.getMessage(o),
37863             raw: o
37864         });
37865
37866         // blow up if no successProperty
37867         if (Ext.isEmpty(res.success)) {
37868             throw new Ext.data.JsonReader.Error('successProperty-response', this.meta.successProperty);
37869         }
37870         return res;
37871     },
37872
37873     /**
37874      * Create a data block containing Ext.data.Records from a JSON object.
37875      * @param {Object} o An object which contains an Array of row objects in the property specified
37876      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
37877      * which contains the total size of the dataset.
37878      * @return {Object} data A data block which is used by an Ext.data.Store object as
37879      * a cache of Ext.data.Records.
37880      */
37881     readRecords : function(o){
37882         /**
37883          * After any data loads, the raw JSON data is available for further custom processing.  If no data is
37884          * loaded or there is a load exception this property will be undefined.
37885          * @type Object
37886          */
37887         this.jsonData = o;
37888         if(o.metaData){
37889             this.onMetaChange(o.metaData);
37890         }
37891         var s = this.meta, Record = this.recordType,
37892             f = Record.prototype.fields, fi = f.items, fl = f.length, v;
37893
37894         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
37895         if(s.totalProperty){
37896             v = parseInt(this.getTotal(o), 10);
37897             if(!isNaN(v)){
37898                 totalRecords = v;
37899             }
37900         }
37901         if(s.successProperty){
37902             v = this.getSuccess(o);
37903             if(v === false || v === 'false'){
37904                 success = false;
37905             }
37906         }
37907
37908         // TODO return Ext.data.Response instance instead.  @see #readResponse
37909         return {
37910             success : success,
37911             records : this.extractData(root, true), // <-- true to return [Ext.data.Record]
37912             totalRecords : totalRecords
37913         };
37914     },
37915
37916     // private
37917     buildExtractors : function() {
37918         if(this.ef){
37919             return;
37920         }
37921         var s = this.meta, Record = this.recordType,
37922             f = Record.prototype.fields, fi = f.items, fl = f.length;
37923
37924         if(s.totalProperty) {
37925             this.getTotal = this.createAccessor(s.totalProperty);
37926         }
37927         if(s.successProperty) {
37928             this.getSuccess = this.createAccessor(s.successProperty);
37929         }
37930         if (s.messageProperty) {
37931             this.getMessage = this.createAccessor(s.messageProperty);
37932         }
37933         this.getRoot = s.root ? this.createAccessor(s.root) : function(p){return p;};
37934         if (s.id || s.idProperty) {
37935             var g = this.createAccessor(s.id || s.idProperty);
37936             this.getId = function(rec) {
37937                 var r = g(rec);
37938                 return (r === undefined || r === '') ? null : r;
37939             };
37940         } else {
37941             this.getId = function(){return null;};
37942         }
37943         var ef = [];
37944         for(var i = 0; i < fl; i++){
37945             f = fi[i];
37946             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
37947             ef.push(this.createAccessor(map));
37948         }
37949         this.ef = ef;
37950     },
37951
37952     /**
37953      * @ignore
37954      * TODO This isn't used anywhere??  Don't we want to use this where possible instead of complex #createAccessor?
37955      */
37956     simpleAccess : function(obj, subsc) {
37957         return obj[subsc];
37958     },
37959
37960     /**
37961      * @ignore
37962      */
37963     createAccessor : function(){
37964         var re = /[\[\.]/;
37965         return function(expr) {
37966             if(Ext.isEmpty(expr)){
37967                 return Ext.emptyFn;
37968             }
37969             if(Ext.isFunction(expr)){
37970                 return expr;
37971             }
37972             var i = String(expr).search(re);
37973             if(i >= 0){
37974                 return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
37975             }
37976             return function(obj){
37977                 return obj[expr];
37978             };
37979
37980         };
37981     }(),
37982
37983     /**
37984      * type-casts a single row of raw-data from server
37985      * @param {Object} data
37986      * @param {Array} items
37987      * @param {Integer} len
37988      * @private
37989      */
37990     extractValues : function(data, items, len) {
37991         var f, values = {};
37992         for(var j = 0; j < len; j++){
37993             f = items[j];
37994             var v = this.ef[j](data);
37995             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
37996         }
37997         return values;
37998     }
37999 });
38000
38001 /**
38002  * @class Ext.data.JsonReader.Error
38003  * Error class for JsonReader
38004  */
38005 Ext.data.JsonReader.Error = Ext.extend(Ext.Error, {
38006     constructor : function(message, arg) {
38007         this.arg = arg;
38008         Ext.Error.call(this, message);
38009     },
38010     name : 'Ext.data.JsonReader'
38011 });
38012 Ext.apply(Ext.data.JsonReader.Error.prototype, {
38013     lang: {
38014         'response': 'An error occurred while json-decoding your server response',
38015         '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.',
38016         '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.',
38017         '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.',
38018         '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.'
38019     }
38020 });
38021 /**
38022  * @class Ext.data.ArrayReader
38023  * @extends Ext.data.JsonReader
38024  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an Array.
38025  * Each element of that Array represents a row of data fields. The
38026  * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
38027  * of the field definition if it exists, or the field's ordinal position in the definition.</p>
38028  * <p>Example code:</p>
38029  * <pre><code>
38030 var Employee = Ext.data.Record.create([
38031     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
38032     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
38033 ]);
38034 var myReader = new Ext.data.ArrayReader({
38035     {@link #idIndex}: 0
38036 }, Employee);
38037 </code></pre>
38038  * <p>This would consume an Array like this:</p>
38039  * <pre><code>
38040 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
38041  * </code></pre>
38042  * @constructor
38043  * Create a new ArrayReader
38044  * @param {Object} meta Metadata configuration options.
38045  * @param {Array/Object} recordType
38046  * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
38047  * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
38048  * constructor created from {@link Ext.data.Record#create}.</p>
38049  */
38050 Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
38051     /**
38052      * @cfg {String} successProperty
38053      * @hide
38054      */
38055     /**
38056      * @cfg {Number} id (optional) The subscript within row Array that provides an ID for the Record.
38057      * Deprecated. Use {@link #idIndex} instead.
38058      */
38059     /**
38060      * @cfg {Number} idIndex (optional) The subscript within row Array that provides an ID for the Record.
38061      */
38062     /**
38063      * Create a data block containing Ext.data.Records from an Array.
38064      * @param {Object} o An Array of row objects which represents the dataset.
38065      * @return {Object} data A data block which is used by an Ext.data.Store object as
38066      * a cache of Ext.data.Records.
38067      */
38068     readRecords : function(o){
38069         this.arrayData = o;
38070         var s = this.meta,
38071             sid = s ? Ext.num(s.idIndex, s.id) : null,
38072             recordType = this.recordType,
38073             fields = recordType.prototype.fields,
38074             records = [],
38075             success = true,
38076             v;
38077
38078         var root = this.getRoot(o);
38079
38080         for(var i = 0, len = root.length; i < len; i++) {
38081             var n = root[i],
38082                 values = {},
38083                 id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
38084             for(var j = 0, jlen = fields.length; j < jlen; j++) {
38085                 var f = fields.items[j],
38086                     k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
38087                 v = n[k] !== undefined ? n[k] : f.defaultValue;
38088                 v = f.convert(v, n);
38089                 values[f.name] = v;
38090             }
38091             var record = new recordType(values, id);
38092             record.json = n;
38093             records[records.length] = record;
38094         }
38095
38096         var totalRecords = records.length;
38097
38098         if(s.totalProperty) {
38099             v = parseInt(this.getTotal(o), 10);
38100             if(!isNaN(v)) {
38101                 totalRecords = v;
38102             }
38103         }
38104         if(s.successProperty){
38105             v = this.getSuccess(o);
38106             if(v === false || v === 'false'){
38107                 success = false;
38108             }
38109         }
38110
38111         return {
38112             success : success,
38113             records : records,
38114             totalRecords : totalRecords
38115         };
38116     }
38117 });/**
38118  * @class Ext.data.ArrayStore
38119  * @extends Ext.data.Store
38120  * <p>Formerly known as "SimpleStore".</p>
38121  * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
38122  * An ArrayStore will be automatically configured with a {@link Ext.data.ArrayReader}.</p>
38123  * <p>A store configuration would be something like:<pre><code>
38124 var store = new Ext.data.ArrayStore({
38125     // store configs
38126     autoDestroy: true,
38127     storeId: 'myStore',
38128     // reader configs
38129     idIndex: 0,  
38130     fields: [
38131        'company',
38132        {name: 'price', type: 'float'},
38133        {name: 'change', type: 'float'},
38134        {name: 'pctChange', type: 'float'},
38135        {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
38136     ]
38137 });
38138  * </code></pre></p>
38139  * <p>This store is configured to consume a returned object of the form:<pre><code>
38140 var myData = [
38141     ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
38142     ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
38143     ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
38144     ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
38145     ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
38146 ];
38147  * </code></pre>
38148  * An object literal of this form could also be used as the {@link #data} config option.</p>
38149  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of 
38150  * <b>{@link Ext.data.ArrayReader ArrayReader}</b>.</p>
38151  * @constructor
38152  * @param {Object} config
38153  * @xtype arraystore
38154  */
38155 Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
38156     /**
38157      * @cfg {Ext.data.DataReader} reader @hide
38158      */
38159     constructor: function(config){
38160         Ext.data.ArrayStore.superclass.constructor.call(this, Ext.apply(config, {
38161             reader: new Ext.data.ArrayReader(config)
38162         }));
38163     },
38164
38165     loadData : function(data, append){
38166         if(this.expandData === true){
38167             var r = [];
38168             for(var i = 0, len = data.length; i < len; i++){
38169                 r[r.length] = [data[i]];
38170             }
38171             data = r;
38172         }
38173         Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
38174     }
38175 });
38176 Ext.reg('arraystore', Ext.data.ArrayStore);
38177
38178 // backwards compat
38179 Ext.data.SimpleStore = Ext.data.ArrayStore;
38180 Ext.reg('simplestore', Ext.data.SimpleStore);/**
38181  * @class Ext.data.JsonStore
38182  * @extends Ext.data.Store
38183  * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
38184  * A JsonStore will be automatically configured with a {@link Ext.data.JsonReader}.</p>
38185  * <p>A store configuration would be something like:<pre><code>
38186 var store = new Ext.data.JsonStore({
38187     // store configs
38188     autoDestroy: true,
38189     url: 'get-images.php',
38190     storeId: 'myStore',
38191     // reader configs
38192     root: 'images',
38193     idProperty: 'name',
38194     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
38195 });
38196  * </code></pre></p>
38197  * <p>This store is configured to consume a returned object of the form:<pre><code>
38198 {
38199     images: [
38200         {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
38201         {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
38202     ]
38203 }
38204  * </code></pre>
38205  * An object literal of this form could also be used as the {@link #data} config option.</p>
38206  * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
38207  * <b>{@link Ext.data.JsonReader JsonReader}</b>.</p>
38208  * @constructor
38209  * @param {Object} config
38210  * @xtype jsonstore
38211  */
38212 Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
38213     /**
38214      * @cfg {Ext.data.DataReader} reader @hide
38215      */
38216     constructor: function(config){
38217         Ext.data.JsonStore.superclass.constructor.call(this, Ext.apply(config, {
38218             reader: new Ext.data.JsonReader(config)
38219         }));
38220     }
38221 });
38222 Ext.reg('jsonstore', Ext.data.JsonStore);/**
38223  * @class Ext.data.XmlWriter
38224  * @extends Ext.data.DataWriter
38225  * DataWriter extension for writing an array or single {@link Ext.data.Record} object(s) in preparation for executing a remote CRUD action via XML.
38226  * 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.
38227  * See the {@link #tpl} configuration-property.
38228  */
38229 Ext.data.XmlWriter = function(params) {
38230     Ext.data.XmlWriter.superclass.constructor.apply(this, arguments);
38231     // compile the XTemplate for rendering XML documents.
38232     this.tpl = (typeof(this.tpl) === 'string') ? new Ext.XTemplate(this.tpl).compile() : this.tpl.compile();
38233 };
38234 Ext.extend(Ext.data.XmlWriter, Ext.data.DataWriter, {
38235     /**
38236      * @cfg {String} documentRoot [xrequest] (Optional) The name of the XML document root-node.  <b>Note:</b>
38237      * this parameter is required </b>only when</b> sending extra {@link Ext.data.Store#baseParams baseParams} to the server
38238      * during a write-request -- if no baseParams are set, the {@link Ext.data.XmlReader#record} meta-property can
38239      * suffice as the XML document root-node for write-actions involving just a <b>single record</b>.  For requests
38240      * involving <b>multiple</b> records and <b>NO</b> baseParams, the {@link Ext.data.XmlWriter#root} property can
38241      * act as the XML document root.
38242      */
38243     documentRoot: 'xrequest',
38244     /**
38245      * @cfg {Boolean} forceDocumentRoot [false] Set to <tt>true</tt> to force XML documents having a root-node as defined
38246      * by {@link #documentRoot}, even with no baseParams defined.
38247      */
38248     forceDocumentRoot: false,
38249     /**
38250      * @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
38251      * xml-record written to the server will be wrapped in an element named after {@link Ext.data.XmlReader#record} property.
38252      * eg:
38253 <code><pre>
38254 &lt;?xml version="1.0" encoding="UTF-8"?>
38255 &lt;user>&lt;first>Barney&lt;/first>&lt;/user>
38256 </code></pre>
38257      * However, when <b>multiple</b> records are written in a batch-operation, these records must be wrapped in a containing
38258      * Element.
38259      * eg:
38260 <code><pre>
38261 &lt;?xml version="1.0" encoding="UTF-8"?>
38262     &lt;records>
38263         &lt;first>Barney&lt;/first>&lt;/user>
38264         &lt;records>&lt;first>Barney&lt;/first>&lt;/user>
38265     &lt;/records>
38266 </code></pre>
38267      * Defaults to <tt>records</tt>.  Do not confuse the nature of this property with that of {@link #documentRoot}
38268      */
38269     root: 'records',
38270     /**
38271      * @cfg {String} xmlVersion [1.0] The <tt>version</tt> written to header of xml documents.
38272 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
38273      */
38274     xmlVersion : '1.0',
38275     /**
38276      * @cfg {String} xmlEncoding [ISO-8859-15] The <tt>encoding</tt> written to header of xml documents.
38277 <code><pre>&lt;?xml version="1.0" encoding="ISO-8859-15"?></pre></code>
38278      */
38279     xmlEncoding: 'ISO-8859-15',
38280     /**
38281      * @cfg {String/Ext.XTemplate} tpl The XML template used to render {@link Ext.data.Api#actions write-actions} to your server.
38282      * <p>One can easily provide his/her own custom {@link Ext.XTemplate#constructor template-definition} if the default does not suffice.</p>
38283      * <p>Defaults to:</p>
38284 <code><pre>
38285 &lt;?xml version="{version}" encoding="{encoding}"?>
38286     &lt;tpl if="documentRoot">&lt;{documentRoot}>
38287     &lt;tpl for="baseParams">
38288         &lt;tpl for=".">
38289             &lt;{name}>{value}&lt;/{name}>
38290         &lt;/tpl>
38291     &lt;/tpl>
38292     &lt;tpl if="records.length &gt; 1">&lt;{root}>',
38293     &lt;tpl for="records">
38294         &lt;{parent.record}>
38295         &lt;tpl for=".">
38296             &lt;{name}>{value}&lt;/{name}>
38297         &lt;/tpl>
38298         &lt;/{parent.record}>
38299     &lt;/tpl>
38300     &lt;tpl if="records.length &gt; 1">&lt;/{root}>&lt;/tpl>
38301     &lt;tpl if="documentRoot">&lt;/{documentRoot}>&lt;/tpl>
38302 </pre></code>
38303      * <p>Templates will be called with the following API</p>
38304      * <ul>
38305      * <li>{String} version [1.0] The xml version.</li>
38306      * <li>{String} encoding [ISO-8859-15] The xml encoding.</li>
38307      * <li>{String/false} documentRoot The XML document root-node name or <tt>false</tt> if not required.  See {@link #documentRoot} and {@link #forceDocumentRoot}.</li>
38308      * <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>
38309      * <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>
38310      * <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.
38311      *     Each item within the records array will contain an array of field objects having the following properties:
38312      *     <ul>
38313      *         <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>
38314      *         <li>{Mixed} value The record value of the field enclosed within XML tags specified by name property above.</li>
38315      *     </ul></li>
38316      * <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>
38317      * </ul>
38318      */
38319     // Encoding the ? here in case it's being included by some kind of page that will parse it (eg. PHP)
38320     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>',
38321
38322
38323     /**
38324      * XmlWriter implementation of the final stage of a write action.
38325      * @param {Object} params Transport-proxy's (eg: {@link Ext.Ajax#request}) params-object to write-to.
38326      * @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}.
38327      * @param {Object/Object[]} data Data-object representing the compiled Store-recordset.
38328      */
38329     render : function(params, baseParams, data) {
38330         baseParams = this.toArray(baseParams);
38331         params.xmlData = this.tpl.applyTemplate({
38332             version: this.xmlVersion,
38333             encoding: this.xmlEncoding,
38334             documentRoot: (baseParams.length > 0 || this.forceDocumentRoot === true) ? this.documentRoot : false,
38335             record: this.meta.record,
38336             root: this.root,
38337             baseParams: baseParams,
38338             records: (Ext.isArray(data[0])) ? data : [data]
38339         });
38340     },
38341
38342     /**
38343      * createRecord
38344      * @protected
38345      * @param {Ext.data.Record} rec
38346      * @return {Array} Array of <tt>name:value</tt> pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
38347      */
38348     createRecord : function(rec) {
38349         return this.toArray(this.toHash(rec));
38350     },
38351
38352     /**
38353      * updateRecord
38354      * @protected
38355      * @param {Ext.data.Record} rec
38356      * @return {Array} Array of {name:value} pairs for attributes of the {@link Ext.data.Record}.  See {@link Ext.data.DataWriter#toHash}.
38357      */
38358     updateRecord : function(rec) {
38359         return this.toArray(this.toHash(rec));
38360
38361     },
38362     /**
38363      * destroyRecord
38364      * @protected
38365      * @param {Ext.data.Record} rec
38366      * @return {Array} Array containing a attribute-object (name/value pair) representing the {@link Ext.data.DataReader#idProperty idProperty}.
38367      */
38368     destroyRecord : function(rec) {
38369         var data = {};
38370         data[this.meta.idProperty] = rec.id;
38371         return this.toArray(data);
38372     }
38373 });
38374 /**
38375  * @class Ext.data.XmlReader
38376  * @extends Ext.data.DataReader
38377  * <p>Data reader class to create an Array of {@link Ext.data.Record} objects from an XML document
38378  * based on mappings in a provided {@link Ext.data.Record} constructor.</p>
38379  * <p><b>Note</b>: that in order for the browser to parse a returned XML document, the Content-Type
38380  * header in the HTTP response must be set to "text/xml" or "application/xml".</p>
38381  * <p>Example code:</p>
38382  * <pre><code>
38383 var Employee = Ext.data.Record.create([
38384    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it is the same as "name"
38385    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
38386 ]);
38387 var myReader = new Ext.data.XmlReader({
38388    totalProperty: "results", // The element which contains the total dataset size (optional)
38389    record: "row",           // The repeated element which contains row information
38390    idProperty: "id"         // The element within the row that provides an ID for the record (optional)
38391    messageProperty: "msg"   // The element within the response that provides a user-feedback message (optional)
38392 }, Employee);
38393 </code></pre>
38394  * <p>
38395  * This would consume an XML file like this:
38396  * <pre><code>
38397 &lt;?xml version="1.0" encoding="UTF-8"?>
38398 &lt;dataset>
38399  &lt;results>2&lt;/results>
38400  &lt;row>
38401    &lt;id>1&lt;/id>
38402    &lt;name>Bill&lt;/name>
38403    &lt;occupation>Gardener&lt;/occupation>
38404  &lt;/row>
38405  &lt;row>
38406    &lt;id>2&lt;/id>
38407    &lt;name>Ben&lt;/name>
38408    &lt;occupation>Horticulturalist&lt;/occupation>
38409  &lt;/row>
38410 &lt;/dataset>
38411 </code></pre>
38412  * @cfg {String} totalProperty The DomQuery path from which to retrieve the total number of records
38413  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
38414  * paged from the remote server.
38415  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
38416  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
38417  * @cfg {String} successProperty The DomQuery path to the success attribute used by forms.
38418  * @cfg {String} idPath The DomQuery path relative from the record element to the element that contains
38419  * a record identifier value.
38420  * @constructor
38421  * Create a new XmlReader.
38422  * @param {Object} meta Metadata configuration options
38423  * @param {Object} recordType Either an Array of field definition objects as passed to
38424  * {@link Ext.data.Record#create}, or a Record constructor object created using {@link Ext.data.Record#create}.
38425  */
38426 Ext.data.XmlReader = function(meta, recordType){
38427     meta = meta || {};
38428
38429     // backwards compat, convert idPath or id / success
38430     Ext.applyIf(meta, {
38431         idProperty: meta.idProperty || meta.idPath || meta.id,
38432         successProperty: meta.successProperty || meta.success
38433     });
38434
38435     Ext.data.XmlReader.superclass.constructor.call(this, meta, recordType || meta.fields);
38436 };
38437 Ext.extend(Ext.data.XmlReader, Ext.data.DataReader, {
38438     /**
38439      * This method is only used by a DataProxy which has retrieved data from a remote server.
38440      * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
38441      * to contain a property called <tt>responseXML</tt> which refers to an XML document object.
38442      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
38443      * a cache of Ext.data.Records.
38444      */
38445     read : function(response){
38446         var doc = response.responseXML;
38447         if(!doc) {
38448             throw {message: "XmlReader.read: XML Document not available"};
38449         }
38450         return this.readRecords(doc);
38451     },
38452
38453     /**
38454      * Create a data block containing Ext.data.Records from an XML document.
38455      * @param {Object} doc A parsed XML document.
38456      * @return {Object} records A data block which is used by an {@link Ext.data.Store} as
38457      * a cache of Ext.data.Records.
38458      */
38459     readRecords : function(doc){
38460         /**
38461          * After any data loads/reads, the raw XML Document is available for further custom processing.
38462          * @type XMLDocument
38463          */
38464         this.xmlData = doc;
38465
38466         var root    = doc.documentElement || doc,
38467             q       = Ext.DomQuery,
38468             totalRecords = 0,
38469             success = true;
38470
38471         if(this.meta.totalProperty){
38472             totalRecords = this.getTotal(root, 0);
38473         }
38474         if(this.meta.successProperty){
38475             success = this.getSuccess(root);
38476         }
38477
38478         var records = this.extractData(q.select(this.meta.record, root), true); // <-- true to return Ext.data.Record[]
38479
38480         // TODO return Ext.data.Response instance.  @see #readResponse
38481         return {
38482             success : success,
38483             records : records,
38484             totalRecords : totalRecords || records.length
38485         };
38486     },
38487
38488     /**
38489      * Decode an XML response from server.
38490      * @param {String} action [{@link Ext.data.Api#actions} create|read|update|destroy]
38491      * @param {Object} response HTTP Response object from browser.
38492      * @return {Ext.data.Response} An instance of {@link Ext.data.Response}
38493      */
38494     readResponse : function(action, response) {
38495         var q   = Ext.DomQuery,
38496         doc     = response.responseXML;
38497
38498         // create general Response instance.
38499         var res = new Ext.data.Response({
38500             action: action,
38501             success : this.getSuccess(doc),
38502             message: this.getMessage(doc),
38503             data: this.extractData(q.select(this.meta.record, doc) || q.select(this.meta.root, doc), false),
38504             raw: doc
38505         });
38506
38507         if (Ext.isEmpty(res.success)) {
38508             throw new Ext.data.DataReader.Error('successProperty-response', this.meta.successProperty);
38509         }
38510
38511         // Create actions from a response having status 200 must return pk
38512         if (action === Ext.data.Api.actions.create) {
38513             var def = Ext.isDefined(res.data);
38514             if (def && Ext.isEmpty(res.data)) {
38515                 throw new Ext.data.JsonReader.Error('root-empty', this.meta.root);
38516             }
38517             else if (!def) {
38518                 throw new Ext.data.JsonReader.Error('root-undefined-response', this.meta.root);
38519             }
38520         }
38521         return res;
38522     },
38523
38524     getSuccess : function() {
38525         return true;
38526     },
38527
38528     /**
38529      * build response-data extractor functions.
38530      * @private
38531      * @ignore
38532      */
38533     buildExtractors : function() {
38534         if(this.ef){
38535             return;
38536         }
38537         var s       = this.meta,
38538             Record  = this.recordType,
38539             f       = Record.prototype.fields,
38540             fi      = f.items,
38541             fl      = f.length;
38542
38543         if(s.totalProperty) {
38544             this.getTotal = this.createAccessor(s.totalProperty);
38545         }
38546         if(s.successProperty) {
38547             this.getSuccess = this.createAccessor(s.successProperty);
38548         }
38549         if (s.messageProperty) {
38550             this.getMessage = this.createAccessor(s.messageProperty);
38551         }
38552         this.getRoot = function(res) {
38553             return (!Ext.isEmpty(res[this.meta.record])) ? res[this.meta.record] : res[this.meta.root];
38554         };
38555         if (s.idPath || s.idProperty) {
38556             var g = this.createAccessor(s.idPath || s.idProperty);
38557             this.getId = function(rec) {
38558                 var id = g(rec) || rec.id;
38559                 return (id === undefined || id === '') ? null : id;
38560             };
38561         } else {
38562             this.getId = function(){return null;};
38563         }
38564         var ef = [];
38565         for(var i = 0; i < fl; i++){
38566             f = fi[i];
38567             var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
38568             ef.push(this.createAccessor(map));
38569         }
38570         this.ef = ef;
38571     },
38572
38573     /**
38574      * Creates a function to return some particular key of data from a response.
38575      * @param {String} key
38576      * @return {Function}
38577      * @private
38578      * @ignore
38579      */
38580     createAccessor : function(){
38581         var q = Ext.DomQuery;
38582         return function(key) {
38583             switch(key) {
38584                 case this.meta.totalProperty:
38585                     return function(root, def){
38586                         return q.selectNumber(key, root, def);
38587                     };
38588                     break;
38589                 case this.meta.successProperty:
38590                     return function(root, def) {
38591                         var sv = q.selectValue(key, root, true);
38592                         var success = sv !== false && sv !== 'false';
38593                         return success;
38594                     };
38595                     break;
38596                 default:
38597                     return function(root, def) {
38598                         return q.selectValue(key, root, def);
38599                     };
38600                     break;
38601             }
38602         };
38603     }(),
38604
38605     /**
38606      * extracts values and type-casts a row of data from server, extracted by #extractData
38607      * @param {Hash} data
38608      * @param {Ext.data.Field[]} items
38609      * @param {Number} len
38610      * @private
38611      * @ignore
38612      */
38613     extractValues : function(data, items, len) {
38614         var f, values = {};
38615         for(var j = 0; j < len; j++){
38616             f = items[j];
38617             var v = this.ef[j](data);
38618             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, data);
38619         }
38620         return values;
38621     }
38622 });/**
38623  * @class Ext.data.XmlStore
38624  * @extends Ext.data.Store
38625  * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.
38626  * A XmlStore will be automatically configured with a {@link Ext.data.XmlReader}.</p>
38627  * <p>A store configuration would be something like:<pre><code>
38628 var store = new Ext.data.XmlStore({
38629     // store configs
38630     autoDestroy: true,
38631     storeId: 'myStore',
38632     url: 'sheldon.xml', // automatically configures a HttpProxy
38633     // reader configs
38634     record: 'Item', // records will have an "Item" tag
38635     idPath: 'ASIN',
38636     totalRecords: '@TotalResults'
38637     fields: [
38638         // set up the fields mapping into the xml doc
38639         // The first needs mapping, the others are very basic
38640         {name: 'Author', mapping: 'ItemAttributes > Author'},
38641         'Title', 'Manufacturer', 'ProductGroup'
38642     ]
38643 });
38644  * </code></pre></p>
38645  * <p>This store is configured to consume a returned object of the form:<pre><code>
38646 &#60?xml version="1.0" encoding="UTF-8"?>
38647 &#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">
38648     &#60Items>
38649         &#60Request>
38650             &#60IsValid>True&#60/IsValid>
38651             &#60ItemSearchRequest>
38652                 &#60Author>Sidney Sheldon&#60/Author>
38653                 &#60SearchIndex>Books&#60/SearchIndex>
38654             &#60/ItemSearchRequest>
38655         &#60/Request>
38656         &#60TotalResults>203&#60/TotalResults>
38657         &#60TotalPages>21&#60/TotalPages>
38658         &#60Item>
38659             &#60ASIN>0446355453&#60/ASIN>
38660             &#60DetailPageURL>
38661                 http://www.amazon.com/
38662             &#60/DetailPageURL>
38663             &#60ItemAttributes>
38664                 &#60Author>Sidney Sheldon&#60/Author>
38665                 &#60Manufacturer>Warner Books&#60/Manufacturer>
38666                 &#60ProductGroup>Book&#60/ProductGroup>
38667                 &#60Title>Master of the Game&#60/Title>
38668             &#60/ItemAttributes>
38669         &#60/Item>
38670     &#60/Items>
38671 &#60/ItemSearchResponse>
38672  * </code></pre>
38673  * An object literal of this form could also be used as the {@link #data} config option.</p>
38674  * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of 
38675  * <b>{@link Ext.data.XmlReader XmlReader}</b>.</p>
38676  * @constructor
38677  * @param {Object} config
38678  * @xtype xmlstore
38679  */
38680 Ext.data.XmlStore = Ext.extend(Ext.data.Store, {
38681     /**
38682      * @cfg {Ext.data.DataReader} reader @hide
38683      */
38684     constructor: function(config){
38685         Ext.data.XmlStore.superclass.constructor.call(this, Ext.apply(config, {
38686             reader: new Ext.data.XmlReader(config)
38687         }));
38688     }
38689 });
38690 Ext.reg('xmlstore', Ext.data.XmlStore);/**
38691  * @class Ext.data.GroupingStore
38692  * @extends Ext.data.Store
38693  * A specialized store implementation that provides for grouping records by one of the available fields. This
38694  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to provide the data model for
38695  * a grouped GridPanel.
38696  *
38697  * Internally, GroupingStore is simply a normal Store with multi sorting enabled from the start. The grouping field
38698  * and direction are always injected as the first sorter pair. GroupingView picks up on the configured groupField and
38699  * builds grid rows appropriately.
38700  *
38701  * @constructor
38702  * Creates a new GroupingStore.
38703  * @param {Object} config A config object containing the objects needed for the Store to access data,
38704  * and read the data into Records.
38705  * @xtype groupingstore
38706  */
38707 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {
38708
38709     //inherit docs
38710     constructor: function(config) {
38711         config = config || {};
38712
38713         //We do some preprocessing here to massage the grouping + sorting options into a single
38714         //multi sort array. If grouping and sorting options are both presented to the constructor,
38715         //the sorters array consists of the grouping sorter object followed by the sorting sorter object
38716         //see Ext.data.Store's sorting functions for details about how multi sorting works
38717         this.hasMultiSort  = true;
38718         this.multiSortInfo = this.multiSortInfo || {sorters: []};
38719
38720         var sorters    = this.multiSortInfo.sorters,
38721             groupField = config.groupField || this.groupField,
38722             sortInfo   = config.sortInfo || this.sortInfo,
38723             groupDir   = config.groupDir || this.groupDir;
38724
38725         //add the grouping sorter object first
38726         if(groupField){
38727             sorters.push({
38728                 field    : groupField,
38729                 direction: groupDir
38730             });
38731         }
38732
38733         //add the sorting sorter object if it is present
38734         if (sortInfo) {
38735             sorters.push(sortInfo);
38736         }
38737
38738         Ext.data.GroupingStore.superclass.constructor.call(this, config);
38739
38740         this.addEvents(
38741           /**
38742            * @event groupchange
38743            * Fired whenever a call to store.groupBy successfully changes the grouping on the store
38744            * @param {Ext.data.GroupingStore} store The grouping store
38745            * @param {String} groupField The field that the store is now grouped by
38746            */
38747           'groupchange'
38748         );
38749
38750         this.applyGroupField();
38751     },
38752
38753     /**
38754      * @cfg {String} groupField
38755      * The field name by which to sort the store's data (defaults to '').
38756      */
38757     /**
38758      * @cfg {Boolean} remoteGroup
38759      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the
38760      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a
38761      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.
38762      */
38763     remoteGroup : false,
38764     /**
38765      * @cfg {Boolean} groupOnSort
38766      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the
38767      * existing sort info (defaults to false).
38768      */
38769     groupOnSort:false,
38770
38771     groupDir : 'ASC',
38772
38773     /**
38774      * Clears any existing grouping and refreshes the data using the default sort.
38775      */
38776     clearGrouping : function(){
38777         this.groupField = false;
38778
38779         if(this.remoteGroup){
38780             if(this.baseParams){
38781                 delete this.baseParams.groupBy;
38782                 delete this.baseParams.groupDir;
38783             }
38784             var lo = this.lastOptions;
38785             if(lo && lo.params){
38786                 delete lo.params.groupBy;
38787                 delete lo.params.groupDir;
38788             }
38789
38790             this.reload();
38791         }else{
38792             this.sort();
38793             this.fireEvent('datachanged', this);
38794         }
38795     },
38796
38797     /**
38798      * Groups the data by the specified field.
38799      * @param {String} field The field name by which to sort the store's data
38800      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed
38801      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)
38802      */
38803     groupBy : function(field, forceRegroup, direction) {
38804         direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
38805
38806         if (this.groupField == field && this.groupDir == direction && !forceRegroup) {
38807             return; // already grouped by this field
38808         }
38809
38810         //check the contents of the first sorter. If the field matches the CURRENT groupField (before it is set to the new one),
38811         //remove the sorter as it is actually the grouper. The new grouper is added back in by this.sort
38812         sorters = this.multiSortInfo.sorters;
38813         if (sorters.length > 0 && sorters[0].field == this.groupField) {
38814             sorters.shift();
38815         }
38816
38817         this.groupField = field;
38818         this.groupDir = direction;
38819         this.applyGroupField();
38820
38821         var fireGroupEvent = function() {
38822             this.fireEvent('groupchange', this, this.getGroupState());
38823         };
38824
38825         if (this.groupOnSort) {
38826             this.sort(field, direction);
38827             fireGroupEvent.call(this);
38828             return;
38829         }
38830
38831         if (this.remoteGroup) {
38832             this.on('load', fireGroupEvent, this, {single: true});
38833             this.reload();
38834         } else {
38835             this.sort(sorters);
38836             fireGroupEvent.call(this);
38837         }
38838     },
38839
38840     //GroupingStore always uses multisorting so we intercept calls to sort here to make sure that our grouping sorter object
38841     //is always injected first.
38842     sort : function(fieldName, dir) {
38843         if (this.remoteSort) {
38844             return Ext.data.GroupingStore.superclass.sort.call(this, fieldName, dir);
38845         }
38846
38847         var sorters = [];
38848
38849         //cater for any existing valid arguments to this.sort, massage them into an array of sorter objects
38850         if (Ext.isArray(arguments[0])) {
38851             sorters = arguments[0];
38852         } else if (fieldName == undefined) {
38853             //we preserve the existing sortInfo here because this.sort is called after
38854             //clearGrouping and there may be existing sorting
38855             sorters = this.sortInfo ? [this.sortInfo] : [];
38856         } else {
38857             //TODO: this is lifted straight from Ext.data.Store's singleSort function. It should instead be
38858             //refactored into a common method if possible
38859             var field = this.fields.get(fieldName);
38860             if (!field) return false;
38861
38862             var name       = field.name,
38863                 sortInfo   = this.sortInfo || null,
38864                 sortToggle = this.sortToggle ? this.sortToggle[name] : null;
38865
38866             if (!dir) {
38867                 if (sortInfo && sortInfo.field == name) { // toggle sort dir
38868                     dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
38869                 } else {
38870                     dir = field.sortDir;
38871                 }
38872             }
38873
38874             this.sortToggle[name] = dir;
38875             this.sortInfo = {field: name, direction: dir};
38876
38877             sorters = [this.sortInfo];
38878         }
38879
38880         //add the grouping sorter object as the first multisort sorter
38881         if (this.groupField) {
38882             sorters.unshift({direction: this.groupDir, field: this.groupField});
38883         }
38884
38885         return this.multiSort.call(this, sorters, dir);
38886     },
38887
38888     /**
38889      * @private
38890      * Saves the current grouping field and direction to this.baseParams and this.lastOptions.params
38891      * if we're using remote grouping. Does not actually perform any grouping - just stores values
38892      */
38893     applyGroupField: function(){
38894         if (this.remoteGroup) {
38895             if(!this.baseParams){
38896                 this.baseParams = {};
38897             }
38898
38899             Ext.apply(this.baseParams, {
38900                 groupBy : this.groupField,
38901                 groupDir: this.groupDir
38902             });
38903
38904             var lo = this.lastOptions;
38905             if (lo && lo.params) {
38906                 lo.params.groupDir = this.groupDir;
38907
38908                 //this is deleted because of a bug reported at http://www.extjs.com/forum/showthread.php?t=82907
38909                 delete lo.params.groupBy;
38910             }
38911         }
38912     },
38913
38914     /**
38915      * @private
38916      * TODO: This function is apparently never invoked anywhere in the framework. It has no documentation
38917      * and should be considered for deletion
38918      */
38919     applyGrouping : function(alwaysFireChange){
38920         if(this.groupField !== false){
38921             this.groupBy(this.groupField, true, this.groupDir);
38922             return true;
38923         }else{
38924             if(alwaysFireChange === true){
38925                 this.fireEvent('datachanged', this);
38926             }
38927             return false;
38928         }
38929     },
38930
38931     /**
38932      * @private
38933      * Returns the grouping field that should be used. If groupOnSort is used this will be sortInfo's field,
38934      * otherwise it will be this.groupField
38935      * @return {String} The group field
38936      */
38937     getGroupState : function(){
38938         return this.groupOnSort && this.groupField !== false ?
38939                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;
38940     }
38941 });
38942 Ext.reg('groupingstore', Ext.data.GroupingStore);
38943 /**
38944  * @class Ext.data.DirectProxy
38945  * @extends Ext.data.DataProxy
38946  */
38947 Ext.data.DirectProxy = function(config){
38948     Ext.apply(this, config);
38949     if(typeof this.paramOrder == 'string'){
38950         this.paramOrder = this.paramOrder.split(/[\s,|]/);
38951     }
38952     Ext.data.DirectProxy.superclass.constructor.call(this, config);
38953 };
38954
38955 Ext.extend(Ext.data.DirectProxy, Ext.data.DataProxy, {
38956     /**
38957      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed
38958      * server side.  Specify the params in the order in which they must be executed on the server-side
38959      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
38960      * comma, or pipe. For example,
38961      * any of the following would be acceptable:<pre><code>
38962 paramOrder: ['param1','param2','param3']
38963 paramOrder: 'param1 param2 param3'
38964 paramOrder: 'param1,param2,param3'
38965 paramOrder: 'param1|param2|param'
38966      </code></pre>
38967      */
38968     paramOrder: undefined,
38969
38970     /**
38971      * @cfg {Boolean} paramsAsHash
38972      * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a
38973      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
38974      */
38975     paramsAsHash: true,
38976
38977     /**
38978      * @cfg {Function} directFn
38979      * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter
38980      * for Store's which will not implement a full CRUD api.
38981      */
38982     directFn : undefined,
38983
38984     /**
38985      * DirectProxy implementation of {@link Ext.data.DataProxy#doRequest}
38986      * @param {String} action The crud action type (create, read, update, destroy)
38987      * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
38988      * @param {Object} params An object containing properties which are to be used as HTTP parameters
38989      * for the request to the remote server.
38990      * @param {Ext.data.DataReader} reader The Reader object which converts the data
38991      * object into a block of Ext.data.Records.
38992      * @param {Function} callback
38993      * <div class="sub-desc"><p>A function to be called after the request.
38994      * The <tt>callback</tt> is passed the following arguments:<ul>
38995      * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
38996      * <li><tt>options</tt>: Options object from the action request</li>
38997      * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
38998      * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
38999      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
39000      * @protected
39001      */
39002     doRequest : function(action, rs, params, reader, callback, scope, options) {
39003         var args = [],
39004             directFn = this.api[action] || this.directFn;
39005
39006         switch (action) {
39007             case Ext.data.Api.actions.create:
39008                 args.push(params.jsonData);             // <-- create(Hash)
39009                 break;
39010             case Ext.data.Api.actions.read:
39011                 // If the method has no parameters, ignore the paramOrder/paramsAsHash.
39012                 if(directFn.directCfg.method.len > 0){
39013                     if(this.paramOrder){
39014                         for(var i = 0, len = this.paramOrder.length; i < len; i++){
39015                             args.push(params[this.paramOrder[i]]);
39016                         }
39017                     }else if(this.paramsAsHash){
39018                         args.push(params);
39019                     }
39020                 }
39021                 break;
39022             case Ext.data.Api.actions.update:
39023                 args.push(params.jsonData);        // <-- update(Hash/Hash[])
39024                 break;
39025             case Ext.data.Api.actions.destroy:
39026                 args.push(params.jsonData);        // <-- destroy(Int/Int[])
39027                 break;
39028         }
39029
39030         var trans = {
39031             params : params || {},
39032             request: {
39033                 callback : callback,
39034                 scope : scope,
39035                 arg : options
39036             },
39037             reader: reader
39038         };
39039
39040         args.push(this.createCallback(action, rs, trans), this);
39041         directFn.apply(window, args);
39042     },
39043
39044     // private
39045     createCallback : function(action, rs, trans) {
39046         var me = this;
39047         return function(result, res) {
39048             if (!res.status) {
39049                 // @deprecated fire loadexception
39050                 if (action === Ext.data.Api.actions.read) {
39051                     me.fireEvent("loadexception", me, trans, res, null);
39052                 }
39053                 me.fireEvent('exception', me, 'remote', action, trans, res, null);
39054                 trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);
39055                 return;
39056             }
39057             if (action === Ext.data.Api.actions.read) {
39058                 me.onRead(action, trans, result, res);
39059             } else {
39060                 me.onWrite(action, trans, result, res, rs);
39061             }
39062         };
39063     },
39064
39065     /**
39066      * Callback for read actions
39067      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
39068      * @param {Object} trans The request transaction object
39069      * @param {Object} result Data object picked out of the server-response.
39070      * @param {Object} res The server response
39071      * @protected
39072      */
39073     onRead : function(action, trans, result, res) {
39074         var records;
39075         try {
39076             records = trans.reader.readRecords(result);
39077         }
39078         catch (ex) {
39079             // @deprecated: Fire old loadexception for backwards-compat.
39080             this.fireEvent("loadexception", this, trans, res, ex);
39081
39082             this.fireEvent('exception', this, 'response', action, trans, res, ex);
39083             trans.request.callback.call(trans.request.scope, null, trans.request.arg, false);
39084             return;
39085         }
39086         this.fireEvent("load", this, res, trans.request.arg);
39087         trans.request.callback.call(trans.request.scope, records, trans.request.arg, true);
39088     },
39089     /**
39090      * Callback for write actions
39091      * @param {String} action [{@link Ext.data.Api#actions create|read|update|destroy}]
39092      * @param {Object} trans The request transaction object
39093      * @param {Object} result Data object picked out of the server-response.
39094      * @param {Object} res The server response
39095      * @param {Ext.data.Record/[Ext.data.Record]} rs The Store resultset associated with the action.
39096      * @protected
39097      */
39098     onWrite : function(action, trans, result, res, rs) {
39099         var data = trans.reader.extractData(trans.reader.getRoot(result), false);
39100         var success = trans.reader.getSuccess(result);
39101         success = (success !== false);
39102         if (success){
39103             this.fireEvent("write", this, action, data, res, rs, trans.request.arg);
39104         }else{
39105             this.fireEvent('exception', this, 'remote', action, trans, result, rs);
39106         }
39107         trans.request.callback.call(trans.request.scope, data, res, success);
39108     }
39109 });
39110 /**
39111  * @class Ext.data.DirectStore
39112  * @extends Ext.data.Store
39113  * <p>Small helper class to create an {@link Ext.data.Store} configured with an
39114  * {@link Ext.data.DirectProxy} and {@link Ext.data.JsonReader} to make interacting
39115  * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.
39116  * To create a different proxy/reader combination create a basic {@link Ext.data.Store}
39117  * configured as needed.</p>
39118  *
39119  * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>
39120  * <div><ul class="mdetail-params">
39121  * <li><b>{@link Ext.data.Store Store}</b></li>
39122  * <div class="sub-desc"><ul class="mdetail-params">
39123  *
39124  * </ul></div>
39125  * <li><b>{@link Ext.data.JsonReader JsonReader}</b></li>
39126  * <div class="sub-desc"><ul class="mdetail-params">
39127  * <li><tt><b>{@link Ext.data.JsonReader#root root}</b></tt></li>
39128  * <li><tt><b>{@link Ext.data.JsonReader#idProperty idProperty}</b></tt></li>
39129  * <li><tt><b>{@link Ext.data.JsonReader#totalProperty totalProperty}</b></tt></li>
39130  * </ul></div>
39131  *
39132  * <li><b>{@link Ext.data.DirectProxy DirectProxy}</b></li>
39133  * <div class="sub-desc"><ul class="mdetail-params">
39134  * <li><tt><b>{@link Ext.data.DirectProxy#directFn directFn}</b></tt></li>
39135  * <li><tt><b>{@link Ext.data.DirectProxy#paramOrder paramOrder}</b></tt></li>
39136  * <li><tt><b>{@link Ext.data.DirectProxy#paramsAsHash paramsAsHash}</b></tt></li>
39137  * </ul></div>
39138  * </ul></div>
39139  *
39140  * @xtype directstore
39141  *
39142  * @constructor
39143  * @param {Object} config
39144  */
39145 Ext.data.DirectStore = Ext.extend(Ext.data.Store, {
39146     constructor : function(config){
39147         // each transaction upon a singe record will generate a distinct Direct transaction since Direct queues them into one Ajax request.
39148         var c = Ext.apply({}, {
39149             batchTransactions: false
39150         }, config);
39151         Ext.data.DirectStore.superclass.constructor.call(this, Ext.apply(c, {
39152             proxy: Ext.isDefined(c.proxy) ? c.proxy : new Ext.data.DirectProxy(Ext.copyTo({}, c, 'paramOrder,paramsAsHash,directFn,api')),
39153             reader: (!Ext.isDefined(c.reader) && c.fields) ? new Ext.data.JsonReader(Ext.copyTo({}, c, 'totalProperty,root,idProperty'), c.fields) : c.reader
39154         }));
39155     }
39156 });
39157 Ext.reg('directstore', Ext.data.DirectStore);
39158 /**
39159  * @class Ext.Direct
39160  * @extends Ext.util.Observable
39161  * <p><b><u>Overview</u></b></p>
39162  *
39163  * <p>Ext.Direct aims to streamline communication between the client and server
39164  * by providing a single interface that reduces the amount of common code
39165  * typically required to validate data and handle returned data packets
39166  * (reading data, error conditions, etc).</p>
39167  *
39168  * <p>The Ext.direct namespace includes several classes for a closer integration
39169  * with the server-side. The Ext.data namespace also includes classes for working
39170  * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>
39171  *
39172  * <p><b><u>Specification</u></b></p>
39173  *
39174  * <p>For additional information consult the
39175  * <a href="http://extjs.com/products/extjs/direct.php">Ext.Direct Specification</a>.</p>
39176  *
39177  * <p><b><u>Providers</u></b></p>
39178  *
39179  * <p>Ext.Direct uses a provider architecture, where one or more providers are
39180  * used to transport data to and from the server. There are several providers
39181  * that exist in the core at the moment:</p><div class="mdetail-params"><ul>
39182  *
39183  * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>
39184  * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>
39185  * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side
39186  * on the client.</li>
39187  * </ul></div>
39188  *
39189  * <p>A provider does not need to be invoked directly, providers are added via
39190  * {@link Ext.Direct}.{@link Ext.Direct#add add}.</p>
39191  *
39192  * <p><b><u>Router</u></b></p>
39193  *
39194  * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client
39195  * to the appropriate server-side method. Because the Ext.Direct API is completely
39196  * platform-agnostic, you could completely swap out a Java based server solution
39197  * and replace it with one that uses C# without changing the client side JavaScript
39198  * at all.</p>
39199  *
39200  * <p><b><u>Server side events</u></b></p>
39201  *
39202  * <p>Custom events from the server may be handled by the client by adding
39203  * listeners, for example:</p>
39204  * <pre><code>
39205 {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}
39206
39207 // add a handler for a 'message' event sent by the server
39208 Ext.Direct.on('message', function(e){
39209     out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));
39210             out.el.scrollTo('t', 100000, true);
39211 });
39212  * </code></pre>
39213  * @singleton
39214  */
39215 Ext.Direct = Ext.extend(Ext.util.Observable, {
39216     /**
39217      * Each event type implements a getData() method. The default event types are:
39218      * <div class="mdetail-params"><ul>
39219      * <li><b><tt>event</tt></b> : Ext.Direct.Event</li>
39220      * <li><b><tt>exception</tt></b> : Ext.Direct.ExceptionEvent</li>
39221      * <li><b><tt>rpc</tt></b> : Ext.Direct.RemotingEvent</li>
39222      * </ul></div>
39223      * @property eventTypes
39224      * @type Object
39225      */
39226
39227     /**
39228      * Four types of possible exceptions which can occur:
39229      * <div class="mdetail-params"><ul>
39230      * <li><b><tt>Ext.Direct.exceptions.TRANSPORT</tt></b> : 'xhr'</li>
39231      * <li><b><tt>Ext.Direct.exceptions.PARSE</tt></b> : 'parse'</li>
39232      * <li><b><tt>Ext.Direct.exceptions.LOGIN</tt></b> : 'login'</li>
39233      * <li><b><tt>Ext.Direct.exceptions.SERVER</tt></b> : 'exception'</li>
39234      * </ul></div>
39235      * @property exceptions
39236      * @type Object
39237      */
39238     exceptions: {
39239         TRANSPORT: 'xhr',
39240         PARSE: 'parse',
39241         LOGIN: 'login',
39242         SERVER: 'exception'
39243     },
39244
39245     // private
39246     constructor: function(){
39247         this.addEvents(
39248             /**
39249              * @event event
39250              * Fires after an event.
39251              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
39252              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39253              */
39254             'event',
39255             /**
39256              * @event exception
39257              * Fires after an event exception.
39258              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
39259              */
39260             'exception'
39261         );
39262         this.transactions = {};
39263         this.providers = {};
39264     },
39265
39266     /**
39267      * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.
39268      * If the provider is not already connected, it will auto-connect.
39269      * <pre><code>
39270 var pollProv = new Ext.direct.PollingProvider({
39271     url: 'php/poll2.php'
39272 });
39273
39274 Ext.Direct.addProvider(
39275     {
39276         "type":"remoting",       // create a {@link Ext.direct.RemotingProvider}
39277         "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.
39278         "actions":{              // each property within the actions object represents a Class
39279             "TestAction":[       // array of methods within each server side Class
39280             {
39281                 "name":"doEcho", // name of method
39282                 "len":1
39283             },{
39284                 "name":"multiply",
39285                 "len":1
39286             },{
39287                 "name":"doForm",
39288                 "formHandler":true, // handle form on server with Ext.Direct.Transaction
39289                 "len":1
39290             }]
39291         },
39292         "namespace":"myApplication",// namespace to create the Remoting Provider in
39293     },{
39294         type: 'polling', // create a {@link Ext.direct.PollingProvider}
39295         url:  'php/poll.php'
39296     },
39297     pollProv // reference to previously created instance
39298 );
39299      * </code></pre>
39300      * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance
39301      * or config object for a Provider) or any number of Provider descriptions as arguments.  Each
39302      * Provider description instructs Ext.Direct how to create client-side stub methods.
39303      */
39304     addProvider : function(provider){
39305         var a = arguments;
39306         if(a.length > 1){
39307             for(var i = 0, len = a.length; i < len; i++){
39308                 this.addProvider(a[i]);
39309             }
39310             return;
39311         }
39312
39313         // if provider has not already been instantiated
39314         if(!provider.events){
39315             provider = new Ext.Direct.PROVIDERS[provider.type](provider);
39316         }
39317         provider.id = provider.id || Ext.id();
39318         this.providers[provider.id] = provider;
39319
39320         provider.on('data', this.onProviderData, this);
39321         provider.on('exception', this.onProviderException, this);
39322
39323
39324         if(!provider.isConnected()){
39325             provider.connect();
39326         }
39327
39328         return provider;
39329     },
39330
39331     /**
39332      * Retrieve a {@link Ext.direct.Provider provider} by the
39333      * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is
39334      * {@link #addProvider added}.
39335      * @param {String} id Unique identifier assigned to the provider when calling {@link #addProvider}
39336      */
39337     getProvider : function(id){
39338         return this.providers[id];
39339     },
39340
39341     removeProvider : function(id){
39342         var provider = id.id ? id : this.providers[id];
39343         provider.un('data', this.onProviderData, this);
39344         provider.un('exception', this.onProviderException, this);
39345         delete this.providers[provider.id];
39346         return provider;
39347     },
39348
39349     addTransaction: function(t){
39350         this.transactions[t.tid] = t;
39351         return t;
39352     },
39353
39354     removeTransaction: function(t){
39355         delete this.transactions[t.tid || t];
39356         return t;
39357     },
39358
39359     getTransaction: function(tid){
39360         return this.transactions[tid.tid || tid];
39361     },
39362
39363     onProviderData : function(provider, e){
39364         if(Ext.isArray(e)){
39365             for(var i = 0, len = e.length; i < len; i++){
39366                 this.onProviderData(provider, e[i]);
39367             }
39368             return;
39369         }
39370         if(e.name && e.name != 'event' && e.name != 'exception'){
39371             this.fireEvent(e.name, e);
39372         }else if(e.type == 'exception'){
39373             this.fireEvent('exception', e);
39374         }
39375         this.fireEvent('event', e, provider);
39376     },
39377
39378     createEvent : function(response, extraProps){
39379         return new Ext.Direct.eventTypes[response.type](Ext.apply(response, extraProps));
39380     }
39381 });
39382 // overwrite impl. with static instance
39383 Ext.Direct = new Ext.Direct();
39384
39385 Ext.Direct.TID = 1;
39386 Ext.Direct.PROVIDERS = {};/**
39387  * @class Ext.Direct.Transaction
39388  * @extends Object
39389  * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>
39390  * @constructor
39391  * @param {Object} config
39392  */
39393 Ext.Direct.Transaction = function(config){
39394     Ext.apply(this, config);
39395     this.tid = ++Ext.Direct.TID;
39396     this.retryCount = 0;
39397 };
39398 Ext.Direct.Transaction.prototype = {
39399     send: function(){
39400         this.provider.queueTransaction(this);
39401     },
39402
39403     retry: function(){
39404         this.retryCount++;
39405         this.send();
39406     },
39407
39408     getProvider: function(){
39409         return this.provider;
39410     }
39411 };Ext.Direct.Event = function(config){
39412     Ext.apply(this, config);
39413 };
39414
39415 Ext.Direct.Event.prototype = {
39416     status: true,
39417     getData: function(){
39418         return this.data;
39419     }
39420 };
39421
39422 Ext.Direct.RemotingEvent = Ext.extend(Ext.Direct.Event, {
39423     type: 'rpc',
39424     getTransaction: function(){
39425         return this.transaction || Ext.Direct.getTransaction(this.tid);
39426     }
39427 });
39428
39429 Ext.Direct.ExceptionEvent = Ext.extend(Ext.Direct.RemotingEvent, {
39430     status: false,
39431     type: 'exception'
39432 });
39433
39434 Ext.Direct.eventTypes = {
39435     'rpc':  Ext.Direct.RemotingEvent,
39436     'event':  Ext.Direct.Event,
39437     'exception':  Ext.Direct.ExceptionEvent
39438 };
39439 /**
39440  * @class Ext.direct.Provider
39441  * @extends Ext.util.Observable
39442  * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>
39443  * 
39444  * <p>For example ExtJs implements the following subclasses:</p>
39445  * <pre><code>
39446 Provider
39447 |
39448 +---{@link Ext.direct.JsonProvider JsonProvider} 
39449     |
39450     +---{@link Ext.direct.PollingProvider PollingProvider}   
39451     |
39452     +---{@link Ext.direct.RemotingProvider RemotingProvider}   
39453  * </code></pre>
39454  * @abstract
39455  */
39456 Ext.direct.Provider = Ext.extend(Ext.util.Observable, {    
39457     /**
39458      * @cfg {String} id
39459      * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).
39460      * You should assign an id if you need to be able to access the provider later and you do
39461      * not have an object reference available, for example:
39462      * <pre><code>
39463 Ext.Direct.addProvider(
39464     {
39465         type: 'polling',
39466         url:  'php/poll.php',
39467         id:   'poll-provider'
39468     }
39469 );
39470      
39471 var p = {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#getProvider getProvider}('poll-provider');
39472 p.disconnect();
39473      * </code></pre>
39474      */
39475         
39476     /**
39477      * @cfg {Number} priority
39478      * Priority of the request. Lower is higher priority, <tt>0</tt> means "duplex" (always on).
39479      * All Providers default to <tt>1</tt> except for PollingProvider which defaults to <tt>3</tt>.
39480      */    
39481     priority: 1,
39482
39483     /**
39484      * @cfg {String} type
39485      * <b>Required</b>, <tt>undefined</tt> by default.  The <tt>type</tt> of provider specified
39486      * to {@link Ext.Direct Ext.Direct}.{@link Ext.Direct#addProvider addProvider} to create a
39487      * new Provider. Acceptable values by default are:<div class="mdetail-params"><ul>
39488      * <li><b><tt>polling</tt></b> : {@link Ext.direct.PollingProvider PollingProvider}</li>
39489      * <li><b><tt>remoting</tt></b> : {@link Ext.direct.RemotingProvider RemotingProvider}</li>
39490      * </ul></div>
39491      */    
39492  
39493     // private
39494     constructor : function(config){
39495         Ext.apply(this, config);
39496         this.addEvents(
39497             /**
39498              * @event connect
39499              * Fires when the Provider connects to the server-side
39500              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39501              */            
39502             'connect',
39503             /**
39504              * @event disconnect
39505              * Fires when the Provider disconnects from the server-side
39506              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39507              */            
39508             'disconnect',
39509             /**
39510              * @event data
39511              * Fires when the Provider receives data from the server-side
39512              * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
39513              * @param {event} e The {@link Ext.Direct#eventTypes Ext.Direct.Event type} that occurred.
39514              */            
39515             'data',
39516             /**
39517              * @event exception
39518              * Fires when the Provider receives an exception from the server-side
39519              */                        
39520             'exception'
39521         );
39522         Ext.direct.Provider.superclass.constructor.call(this, config);
39523     },
39524
39525     /**
39526      * Returns whether or not the server-side is currently connected.
39527      * Abstract method for subclasses to implement.
39528      */
39529     isConnected: function(){
39530         return false;
39531     },
39532
39533     /**
39534      * Abstract methods for subclasses to implement.
39535      */
39536     connect: Ext.emptyFn,
39537     
39538     /**
39539      * Abstract methods for subclasses to implement.
39540      */
39541     disconnect: Ext.emptyFn
39542 });
39543 /**
39544  * @class Ext.direct.JsonProvider
39545  * @extends Ext.direct.Provider
39546  */
39547 Ext.direct.JsonProvider = Ext.extend(Ext.direct.Provider, {
39548     parseResponse: function(xhr){
39549         if(!Ext.isEmpty(xhr.responseText)){
39550             if(typeof xhr.responseText == 'object'){
39551                 return xhr.responseText;
39552             }
39553             return Ext.decode(xhr.responseText);
39554         }
39555         return null;
39556     },
39557
39558     getEvents: function(xhr){
39559         var data = null;
39560         try{
39561             data = this.parseResponse(xhr);
39562         }catch(e){
39563             var event = new Ext.Direct.ExceptionEvent({
39564                 data: e,
39565                 xhr: xhr,
39566                 code: Ext.Direct.exceptions.PARSE,
39567                 message: 'Error parsing json response: \n\n ' + data
39568             });
39569             return [event];
39570         }
39571         var events = [];
39572         if(Ext.isArray(data)){
39573             for(var i = 0, len = data.length; i < len; i++){
39574                 events.push(Ext.Direct.createEvent(data[i]));
39575             }
39576         }else{
39577             events.push(Ext.Direct.createEvent(data));
39578         }
39579         return events;
39580     }
39581 });/**
39582  * @class Ext.direct.PollingProvider
39583  * @extends Ext.direct.JsonProvider
39584  *
39585  * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.
39586  * The initial request for data originates from the client, and then is responded to by the
39587  * server.</p>
39588  * 
39589  * <p>All configurations for the PollingProvider should be generated by the server-side
39590  * API portion of the Ext.Direct stack.</p>
39591  *
39592  * <p>An instance of PollingProvider may be created directly via the new keyword or by simply
39593  * specifying <tt>type = 'polling'</tt>.  For example:</p>
39594  * <pre><code>
39595 var pollA = new Ext.direct.PollingProvider({
39596     type:'polling',
39597     url: 'php/pollA.php',
39598 });
39599 Ext.Direct.addProvider(pollA);
39600 pollA.disconnect();
39601
39602 Ext.Direct.addProvider(
39603     {
39604         type:'polling',
39605         url: 'php/pollB.php',
39606         id: 'pollB-provider'
39607     }
39608 );
39609 var pollB = Ext.Direct.getProvider('pollB-provider');
39610  * </code></pre>
39611  */
39612 Ext.direct.PollingProvider = Ext.extend(Ext.direct.JsonProvider, {
39613     /**
39614      * @cfg {Number} priority
39615      * Priority of the request (defaults to <tt>3</tt>). See {@link Ext.direct.Provider#priority}.
39616      */
39617     // override default priority
39618     priority: 3,
39619     
39620     /**
39621      * @cfg {Number} interval
39622      * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every
39623      * 3 seconds).
39624      */
39625     interval: 3000,
39626
39627     /**
39628      * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
39629      * on every polling request
39630      */
39631     
39632     /**
39633      * @cfg {String/Function} url
39634      * The url which the PollingProvider should contact with each request. This can also be
39635      * an imported Ext.Direct method which will accept the baseParams as its only argument.
39636      */
39637
39638     // private
39639     constructor : function(config){
39640         Ext.direct.PollingProvider.superclass.constructor.call(this, config);
39641         this.addEvents(
39642             /**
39643              * @event beforepoll
39644              * Fired immediately before a poll takes place, an event handler can return false
39645              * in order to cancel the poll.
39646              * @param {Ext.direct.PollingProvider}
39647              */
39648             'beforepoll',            
39649             /**
39650              * @event poll
39651              * This event has not yet been implemented.
39652              * @param {Ext.direct.PollingProvider}
39653              */
39654             'poll'
39655         );
39656     },
39657
39658     // inherited
39659     isConnected: function(){
39660         return !!this.pollTask;
39661     },
39662
39663     /**
39664      * Connect to the server-side and begin the polling process. To handle each
39665      * response subscribe to the data event.
39666      */
39667     connect: function(){
39668         if(this.url && !this.pollTask){
39669             this.pollTask = Ext.TaskMgr.start({
39670                 run: function(){
39671                     if(this.fireEvent('beforepoll', this) !== false){
39672                         if(typeof this.url == 'function'){
39673                             this.url(this.baseParams);
39674                         }else{
39675                             Ext.Ajax.request({
39676                                 url: this.url,
39677                                 callback: this.onData,
39678                                 scope: this,
39679                                 params: this.baseParams
39680                             });
39681                         }
39682                     }
39683                 },
39684                 interval: this.interval,
39685                 scope: this
39686             });
39687             this.fireEvent('connect', this);
39688         }else if(!this.url){
39689             throw 'Error initializing PollingProvider, no url configured.';
39690         }
39691     },
39692
39693     /**
39694      * Disconnect from the server-side and stop the polling process. The disconnect
39695      * event will be fired on a successful disconnect.
39696      */
39697     disconnect: function(){
39698         if(this.pollTask){
39699             Ext.TaskMgr.stop(this.pollTask);
39700             delete this.pollTask;
39701             this.fireEvent('disconnect', this);
39702         }
39703     },
39704
39705     // private
39706     onData: function(opt, success, xhr){
39707         if(success){
39708             var events = this.getEvents(xhr);
39709             for(var i = 0, len = events.length; i < len; i++){
39710                 var e = events[i];
39711                 this.fireEvent('data', this, e);
39712             }
39713         }else{
39714             var e = new Ext.Direct.ExceptionEvent({
39715                 data: e,
39716                 code: Ext.Direct.exceptions.TRANSPORT,
39717                 message: 'Unable to connect to the server.',
39718                 xhr: xhr
39719             });
39720             this.fireEvent('data', this, e);
39721         }
39722     }
39723 });
39724
39725 Ext.Direct.PROVIDERS['polling'] = Ext.direct.PollingProvider;/**
39726  * @class Ext.direct.RemotingProvider
39727  * @extends Ext.direct.JsonProvider
39728  * 
39729  * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
39730  * server side methods on the client (a remote procedure call (RPC) type of
39731  * connection where the client can initiate a procedure on the server).</p>
39732  * 
39733  * <p>This allows for code to be organized in a fashion that is maintainable,
39734  * while providing a clear path between client and server, something that is
39735  * not always apparent when using URLs.</p>
39736  * 
39737  * <p>To accomplish this the server-side needs to describe what classes and methods
39738  * are available on the client-side. This configuration will typically be
39739  * outputted by the server-side Ext.Direct stack when the API description is built.</p>
39740  */
39741 Ext.direct.RemotingProvider = Ext.extend(Ext.direct.JsonProvider, {       
39742     /**
39743      * @cfg {Object} actions
39744      * Object literal defining the server side actions and methods. For example, if
39745      * the Provider is configured with:
39746      * <pre><code>
39747 "actions":{ // each property within the 'actions' object represents a server side Class 
39748     "TestAction":[ // array of methods within each server side Class to be   
39749     {              // stubbed out on client
39750         "name":"doEcho", 
39751         "len":1            
39752     },{
39753         "name":"multiply",// name of method
39754         "len":2           // The number of parameters that will be used to create an
39755                           // array of data to send to the server side function.
39756                           // Ensure the server sends back a Number, not a String. 
39757     },{
39758         "name":"doForm",
39759         "formHandler":true, // direct the client to use specialized form handling method 
39760         "len":1
39761     }]
39762 }
39763      * </code></pre>
39764      * <p>Note that a Store is not required, a server method can be called at any time.
39765      * In the following example a <b>client side</b> handler is used to call the
39766      * server side method "multiply" in the server-side "TestAction" Class:</p>
39767      * <pre><code>
39768 TestAction.multiply(
39769     2, 4, // pass two arguments to server, so specify len=2
39770     // callback function after the server is called
39771     // result: the result returned by the server
39772     //      e: Ext.Direct.RemotingEvent object
39773     function(result, e){
39774         var t = e.getTransaction();
39775         var action = t.action; // server side Class called
39776         var method = t.method; // server side method called
39777         if(e.status){
39778             var answer = Ext.encode(result); // 8
39779     
39780         }else{
39781             var msg = e.message; // failure message
39782         }
39783     }
39784 );
39785      * </code></pre>
39786      * In the example above, the server side "multiply" function will be passed two
39787      * arguments (2 and 4).  The "multiply" method should return the value 8 which will be
39788      * available as the <tt>result</tt> in the example above. 
39789      */
39790     
39791     /**
39792      * @cfg {String/Object} namespace
39793      * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
39794      * Explicitly specify the namespace Object, or specify a String to have a
39795      * {@link Ext#namespace namespace created} implicitly.
39796      */
39797     
39798     /**
39799      * @cfg {String} url
39800      * <b>Required<b>. The url to connect to the {@link Ext.Direct} server-side router. 
39801      */
39802     
39803     /**
39804      * @cfg {String} enableUrlEncode
39805      * Specify which param will hold the arguments for the method.
39806      * Defaults to <tt>'data'</tt>.
39807      */
39808     
39809     /**
39810      * @cfg {Number/Boolean} enableBuffer
39811      * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
39812      * calls. If a number is specified this is the amount of time in milliseconds
39813      * to wait before sending a batched request (defaults to <tt>10</tt>).</p>
39814      * <br><p>Calls which are received within the specified timeframe will be
39815      * concatenated together and sent in a single request, optimizing the
39816      * application by reducing the amount of round trips that have to be made
39817      * to the server.</p>
39818      */
39819     enableBuffer: 10,
39820     
39821     /**
39822      * @cfg {Number} maxRetries
39823      * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.
39824      */
39825     maxRetries: 1,
39826     
39827     /**
39828      * @cfg {Number} timeout
39829      * The timeout to use for each request. Defaults to <tt>undefined</tt>.
39830      */
39831     timeout: undefined,
39832
39833     constructor : function(config){
39834         Ext.direct.RemotingProvider.superclass.constructor.call(this, config);
39835         this.addEvents(
39836             /**
39837              * @event beforecall
39838              * Fires immediately before the client-side sends off the RPC call.
39839              * By returning false from an event handler you can prevent the call from
39840              * executing.
39841              * @param {Ext.direct.RemotingProvider} provider
39842              * @param {Ext.Direct.Transaction} transaction
39843              */            
39844             'beforecall',            
39845             /**
39846              * @event call
39847              * Fires immediately after the request to the server-side is sent. This does
39848              * NOT fire after the response has come back from the call.
39849              * @param {Ext.direct.RemotingProvider} provider
39850              * @param {Ext.Direct.Transaction} transaction
39851              */            
39852             'call'
39853         );
39854         this.namespace = (Ext.isString(this.namespace)) ? Ext.ns(this.namespace) : this.namespace || window;
39855         this.transactions = {};
39856         this.callBuffer = [];
39857     },
39858
39859     // private
39860     initAPI : function(){
39861         var o = this.actions;
39862         for(var c in o){
39863             var cls = this.namespace[c] || (this.namespace[c] = {}),
39864                 ms = o[c];
39865             for(var i = 0, len = ms.length; i < len; i++){
39866                 var m = ms[i];
39867                 cls[m.name] = this.createMethod(c, m);
39868             }
39869         }
39870     },
39871
39872     // inherited
39873     isConnected: function(){
39874         return !!this.connected;
39875     },
39876
39877     connect: function(){
39878         if(this.url){
39879             this.initAPI();
39880             this.connected = true;
39881             this.fireEvent('connect', this);
39882         }else if(!this.url){
39883             throw 'Error initializing RemotingProvider, no url configured.';
39884         }
39885     },
39886
39887     disconnect: function(){
39888         if(this.connected){
39889             this.connected = false;
39890             this.fireEvent('disconnect', this);
39891         }
39892     },
39893
39894     onData: function(opt, success, xhr){
39895         if(success){
39896             var events = this.getEvents(xhr);
39897             for(var i = 0, len = events.length; i < len; i++){
39898                 var e = events[i],
39899                     t = this.getTransaction(e);
39900                 this.fireEvent('data', this, e);
39901                 if(t){
39902                     this.doCallback(t, e, true);
39903                     Ext.Direct.removeTransaction(t);
39904                 }
39905             }
39906         }else{
39907             var ts = [].concat(opt.ts);
39908             for(var i = 0, len = ts.length; i < len; i++){
39909                 var t = this.getTransaction(ts[i]);
39910                 if(t && t.retryCount < this.maxRetries){
39911                     t.retry();
39912                 }else{
39913                     var e = new Ext.Direct.ExceptionEvent({
39914                         data: e,
39915                         transaction: t,
39916                         code: Ext.Direct.exceptions.TRANSPORT,
39917                         message: 'Unable to connect to the server.',
39918                         xhr: xhr
39919                     });
39920                     this.fireEvent('data', this, e);
39921                     if(t){
39922                         this.doCallback(t, e, false);
39923                         Ext.Direct.removeTransaction(t);
39924                     }
39925                 }
39926             }
39927         }
39928     },
39929
39930     getCallData: function(t){
39931         return {
39932             action: t.action,
39933             method: t.method,
39934             data: t.data,
39935             type: 'rpc',
39936             tid: t.tid
39937         };
39938     },
39939
39940     doSend : function(data){
39941         var o = {
39942             url: this.url,
39943             callback: this.onData,
39944             scope: this,
39945             ts: data,
39946             timeout: this.timeout
39947         }, callData;
39948
39949         if(Ext.isArray(data)){
39950             callData = [];
39951             for(var i = 0, len = data.length; i < len; i++){
39952                 callData.push(this.getCallData(data[i]));
39953             }
39954         }else{
39955             callData = this.getCallData(data);
39956         }
39957
39958         if(this.enableUrlEncode){
39959             var params = {};
39960             params[Ext.isString(this.enableUrlEncode) ? this.enableUrlEncode : 'data'] = Ext.encode(callData);
39961             o.params = params;
39962         }else{
39963             o.jsonData = callData;
39964         }
39965         Ext.Ajax.request(o);
39966     },
39967
39968     combineAndSend : function(){
39969         var len = this.callBuffer.length;
39970         if(len > 0){
39971             this.doSend(len == 1 ? this.callBuffer[0] : this.callBuffer);
39972             this.callBuffer = [];
39973         }
39974     },
39975
39976     queueTransaction: function(t){
39977         if(t.form){
39978             this.processForm(t);
39979             return;
39980         }
39981         this.callBuffer.push(t);
39982         if(this.enableBuffer){
39983             if(!this.callTask){
39984                 this.callTask = new Ext.util.DelayedTask(this.combineAndSend, this);
39985             }
39986             this.callTask.delay(Ext.isNumber(this.enableBuffer) ? this.enableBuffer : 10);
39987         }else{
39988             this.combineAndSend();
39989         }
39990     },
39991
39992     doCall : function(c, m, args){
39993         var data = null, hs = args[m.len], scope = args[m.len+1];
39994
39995         if(m.len !== 0){
39996             data = args.slice(0, m.len);
39997         }
39998
39999         var t = new Ext.Direct.Transaction({
40000             provider: this,
40001             args: args,
40002             action: c,
40003             method: m.name,
40004             data: data,
40005             cb: scope && Ext.isFunction(hs) ? hs.createDelegate(scope) : hs
40006         });
40007
40008         if(this.fireEvent('beforecall', this, t) !== false){
40009             Ext.Direct.addTransaction(t);
40010             this.queueTransaction(t);
40011             this.fireEvent('call', this, t);
40012         }
40013     },
40014
40015     doForm : function(c, m, form, callback, scope){
40016         var t = new Ext.Direct.Transaction({
40017             provider: this,
40018             action: c,
40019             method: m.name,
40020             args:[form, callback, scope],
40021             cb: scope && Ext.isFunction(callback) ? callback.createDelegate(scope) : callback,
40022             isForm: true
40023         });
40024
40025         if(this.fireEvent('beforecall', this, t) !== false){
40026             Ext.Direct.addTransaction(t);
40027             var isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data',
40028                 params = {
40029                     extTID: t.tid,
40030                     extAction: c,
40031                     extMethod: m.name,
40032                     extType: 'rpc',
40033                     extUpload: String(isUpload)
40034                 };
40035             
40036             // change made from typeof callback check to callback.params
40037             // to support addl param passing in DirectSubmit EAC 6/2
40038             Ext.apply(t, {
40039                 form: Ext.getDom(form),
40040                 isUpload: isUpload,
40041                 params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
40042             });
40043             this.fireEvent('call', this, t);
40044             this.processForm(t);
40045         }
40046     },
40047     
40048     processForm: function(t){
40049         Ext.Ajax.request({
40050             url: this.url,
40051             params: t.params,
40052             callback: this.onData,
40053             scope: this,
40054             form: t.form,
40055             isUpload: t.isUpload,
40056             ts: t
40057         });
40058     },
40059
40060     createMethod : function(c, m){
40061         var f;
40062         if(!m.formHandler){
40063             f = function(){
40064                 this.doCall(c, m, Array.prototype.slice.call(arguments, 0));
40065             }.createDelegate(this);
40066         }else{
40067             f = function(form, callback, scope){
40068                 this.doForm(c, m, form, callback, scope);
40069             }.createDelegate(this);
40070         }
40071         f.directCfg = {
40072             action: c,
40073             method: m
40074         };
40075         return f;
40076     },
40077
40078     getTransaction: function(opt){
40079         return opt && opt.tid ? Ext.Direct.getTransaction(opt.tid) : null;
40080     },
40081
40082     doCallback: function(t, e){
40083         var fn = e.status ? 'success' : 'failure';
40084         if(t && t.cb){
40085             var hs = t.cb,
40086                 result = Ext.isDefined(e.result) ? e.result : e.data;
40087             if(Ext.isFunction(hs)){
40088                 hs(result, e);
40089             } else{
40090                 Ext.callback(hs[fn], hs.scope, [result, e]);
40091                 Ext.callback(hs.callback, hs.scope, [result, e]);
40092             }
40093         }
40094     }
40095 });
40096 Ext.Direct.PROVIDERS['remoting'] = Ext.direct.RemotingProvider;/**
40097  * @class Ext.Resizable
40098  * @extends Ext.util.Observable
40099  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
40100  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
40101  * 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
40102  * the element will be wrapped for you automatically.</p>
40103  * <p>Here is the list of valid resize handles:</p>
40104  * <pre>
40105 Value   Description
40106 ------  -------------------
40107  'n'     north
40108  's'     south
40109  'e'     east
40110  'w'     west
40111  'nw'    northwest
40112  'sw'    southwest
40113  'se'    southeast
40114  'ne'    northeast
40115  'all'   all
40116 </pre>
40117  * <p>Here's an example showing the creation of a typical Resizable:</p>
40118  * <pre><code>
40119 var resizer = new Ext.Resizable('element-id', {
40120     handles: 'all',
40121     minWidth: 200,
40122     minHeight: 100,
40123     maxWidth: 500,
40124     maxHeight: 400,
40125     pinned: true
40126 });
40127 resizer.on('resize', myHandler);
40128 </code></pre>
40129  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
40130  * resizer.east.setDisplayed(false);</p>
40131  * @constructor
40132  * Create a new resizable component
40133  * @param {Mixed} el The id or element to resize
40134  * @param {Object} config configuration options
40135   */
40136 Ext.Resizable = Ext.extend(Ext.util.Observable, {
40137
40138     constructor: function(el, config){
40139         this.el = Ext.get(el);
40140         if(config && config.wrap){
40141             config.resizeChild = this.el;
40142             this.el = this.el.wrap(typeof config.wrap == 'object' ? config.wrap : {cls:'xresizable-wrap'});
40143             this.el.id = this.el.dom.id = config.resizeChild.id + '-rzwrap';
40144             this.el.setStyle('overflow', 'hidden');
40145             this.el.setPositioning(config.resizeChild.getPositioning());
40146             config.resizeChild.clearPositioning();
40147             if(!config.width || !config.height){
40148                 var csize = config.resizeChild.getSize();
40149                 this.el.setSize(csize.width, csize.height);
40150             }
40151             if(config.pinned && !config.adjustments){
40152                 config.adjustments = 'auto';
40153             }
40154         }
40155
40156         /**
40157          * The proxy Element that is resized in place of the real Element during the resize operation.
40158          * This may be queried using {@link Ext.Element#getBox} to provide the new area to resize to.
40159          * Read only.
40160          * @type Ext.Element.
40161          * @property proxy
40162          */
40163         this.proxy = this.el.createProxy({tag: 'div', cls: 'x-resizable-proxy', id: this.el.id + '-rzproxy'}, Ext.getBody());
40164         this.proxy.unselectable();
40165         this.proxy.enableDisplayMode('block');
40166
40167         Ext.apply(this, config);
40168
40169         if(this.pinned){
40170             this.disableTrackOver = true;
40171             this.el.addClass('x-resizable-pinned');
40172         }
40173         // if the element isn't positioned, make it relative
40174         var position = this.el.getStyle('position');
40175         if(position != 'absolute' && position != 'fixed'){
40176             this.el.setStyle('position', 'relative');
40177         }
40178         if(!this.handles){ // no handles passed, must be legacy style
40179             this.handles = 's,e,se';
40180             if(this.multiDirectional){
40181                 this.handles += ',n,w';
40182             }
40183         }
40184         if(this.handles == 'all'){
40185             this.handles = 'n s e w ne nw se sw';
40186         }
40187         var hs = this.handles.split(/\s*?[,;]\s*?| /);
40188         var ps = Ext.Resizable.positions;
40189         for(var i = 0, len = hs.length; i < len; i++){
40190             if(hs[i] && ps[hs[i]]){
40191                 var pos = ps[hs[i]];
40192                 this[pos] = new Ext.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent, this.handleCls);
40193             }
40194         }
40195         // legacy
40196         this.corner = this.southeast;
40197
40198         if(this.handles.indexOf('n') != -1 || this.handles.indexOf('w') != -1){
40199             this.updateBox = true;
40200         }
40201
40202         this.activeHandle = null;
40203
40204         if(this.resizeChild){
40205             if(typeof this.resizeChild == 'boolean'){
40206                 this.resizeChild = Ext.get(this.el.dom.firstChild, true);
40207             }else{
40208                 this.resizeChild = Ext.get(this.resizeChild, true);
40209             }
40210         }
40211
40212         if(this.adjustments == 'auto'){
40213             var rc = this.resizeChild;
40214             var hw = this.west, he = this.east, hn = this.north, hs = this.south;
40215             if(rc && (hw || hn)){
40216                 rc.position('relative');
40217                 rc.setLeft(hw ? hw.el.getWidth() : 0);
40218                 rc.setTop(hn ? hn.el.getHeight() : 0);
40219             }
40220             this.adjustments = [
40221                 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
40222                 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
40223             ];
40224         }
40225
40226         if(this.draggable){
40227             this.dd = this.dynamic ?
40228                 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
40229             this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
40230             if(this.constrainTo){
40231                 this.dd.constrainTo(this.constrainTo);
40232             }
40233         }
40234
40235         this.addEvents(
40236             /**
40237              * @event beforeresize
40238              * Fired before resize is allowed. Set {@link #enabled} to false to cancel resize.
40239              * @param {Ext.Resizable} this
40240              * @param {Ext.EventObject} e The mousedown event
40241              */
40242             'beforeresize',
40243             /**
40244              * @event resize
40245              * Fired after a resize.
40246              * @param {Ext.Resizable} this
40247              * @param {Number} width The new width
40248              * @param {Number} height The new height
40249              * @param {Ext.EventObject} e The mouseup event
40250              */
40251             'resize'
40252         );
40253
40254         if(this.width !== null && this.height !== null){
40255             this.resizeTo(this.width, this.height);
40256         }else{
40257             this.updateChildSize();
40258         }
40259         if(Ext.isIE){
40260             this.el.dom.style.zoom = 1;
40261         }
40262         Ext.Resizable.superclass.constructor.call(this);
40263     },
40264
40265     /**
40266      * @cfg {Array/String} adjustments String 'auto' or an array [width, height] with values to be <b>added</b> to the
40267      * resize operation's new size (defaults to <tt>[0, 0]</tt>)
40268      */
40269     adjustments : [0, 0],
40270     /**
40271      * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
40272      */
40273     animate : false,
40274     /**
40275      * @cfg {Mixed} constrainTo Constrain the resize to a particular element
40276      */
40277     /**
40278      * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
40279      */
40280     disableTrackOver : false,
40281     /**
40282      * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
40283      */
40284     draggable: false,
40285     /**
40286      * @cfg {Number} duration Animation duration if animate = true (defaults to 0.35)
40287      */
40288     duration : 0.35,
40289     /**
40290      * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
40291      */
40292     dynamic : false,
40293     /**
40294      * @cfg {String} easing Animation easing if animate = true (defaults to <tt>'easingOutStrong'</tt>)
40295      */
40296     easing : 'easeOutStrong',
40297     /**
40298      * @cfg {Boolean} enabled False to disable resizing (defaults to true)
40299      */
40300     enabled : true,
40301     /**
40302      * @property enabled Writable. False if resizing is disabled.
40303      * @type Boolean
40304      */
40305     /**
40306      * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined).
40307      * Specify either <tt>'all'</tt> or any of <tt>'n s e w ne nw se sw'</tt>.
40308      */
40309     handles : false,
40310     /**
40311      * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  Deprecated style of adding multi-direction resize handles.
40312      */
40313     multiDirectional : false,
40314     /**
40315      * @cfg {Number} height The height of the element in pixels (defaults to null)
40316      */
40317     height : null,
40318     /**
40319      * @cfg {Number} width The width of the element in pixels (defaults to null)
40320      */
40321     width : null,
40322     /**
40323      * @cfg {Number} heightIncrement The increment to snap the height resize in pixels
40324      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.
40325      */
40326     heightIncrement : 0,
40327     /**
40328      * @cfg {Number} widthIncrement The increment to snap the width resize in pixels
40329      * (only applies if <code>{@link #dynamic}==true</code>). Defaults to <tt>0</tt>.
40330      */
40331     widthIncrement : 0,
40332     /**
40333      * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
40334      */
40335     minHeight : 5,
40336     /**
40337      * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
40338      */
40339     minWidth : 5,
40340     /**
40341      * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
40342      */
40343     maxHeight : 10000,
40344     /**
40345      * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
40346      */
40347     maxWidth : 10000,
40348     /**
40349      * @cfg {Number} minX The minimum x for the element (defaults to 0)
40350      */
40351     minX: 0,
40352     /**
40353      * @cfg {Number} minY The minimum x for the element (defaults to 0)
40354      */
40355     minY: 0,
40356     /**
40357      * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
40358      * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
40359      */
40360     pinned : false,
40361     /**
40362      * @cfg {Boolean} preserveRatio True to preserve the original ratio between height
40363      * and width during resize (defaults to false)
40364      */
40365     preserveRatio : false,
40366     /**
40367      * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
40368      */
40369     resizeChild : false,
40370     /**
40371      * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
40372      */
40373     transparent: false,
40374     /**
40375      * @cfg {Ext.lib.Region} resizeRegion Constrain the resize to a particular region
40376      */
40377     /**
40378      * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
40379      * in favor of the handles config option (defaults to false)
40380      */
40381     /**
40382      * @cfg {String} handleCls A css class to add to each handle. Defaults to <tt>''</tt>.
40383      */
40384
40385
40386     /**
40387      * Perform a manual resize and fires the 'resize' event.
40388      * @param {Number} width
40389      * @param {Number} height
40390      */
40391     resizeTo : function(width, height){
40392         this.el.setSize(width, height);
40393         this.updateChildSize();
40394         this.fireEvent('resize', this, width, height, null);
40395     },
40396
40397     // private
40398     startSizing : function(e, handle){
40399         this.fireEvent('beforeresize', this, e);
40400         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
40401
40402             if(!this.overlay){
40403                 this.overlay = this.el.createProxy({tag: 'div', cls: 'x-resizable-overlay', html: '&#160;'}, Ext.getBody());
40404                 this.overlay.unselectable();
40405                 this.overlay.enableDisplayMode('block');
40406                 this.overlay.on({
40407                     scope: this,
40408                     mousemove: this.onMouseMove,
40409                     mouseup: this.onMouseUp
40410                 });
40411             }
40412             this.overlay.setStyle('cursor', handle.el.getStyle('cursor'));
40413
40414             this.resizing = true;
40415             this.startBox = this.el.getBox();
40416             this.startPoint = e.getXY();
40417             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
40418                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
40419
40420             this.overlay.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
40421             this.overlay.show();
40422
40423             if(this.constrainTo) {
40424                 var ct = Ext.get(this.constrainTo);
40425                 this.resizeRegion = ct.getRegion().adjust(
40426                     ct.getFrameWidth('t'),
40427                     ct.getFrameWidth('l'),
40428                     -ct.getFrameWidth('b'),
40429                     -ct.getFrameWidth('r')
40430                 );
40431             }
40432
40433             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
40434             this.proxy.show();
40435             this.proxy.setBox(this.startBox);
40436             if(!this.dynamic){
40437                 this.proxy.setStyle('visibility', 'visible');
40438             }
40439         }
40440     },
40441
40442     // private
40443     onMouseDown : function(handle, e){
40444         if(this.enabled){
40445             e.stopEvent();
40446             this.activeHandle = handle;
40447             this.startSizing(e, handle);
40448         }
40449     },
40450
40451     // private
40452     onMouseUp : function(e){
40453         this.activeHandle = null;
40454         var size = this.resizeElement();
40455         this.resizing = false;
40456         this.handleOut();
40457         this.overlay.hide();
40458         this.proxy.hide();
40459         this.fireEvent('resize', this, size.width, size.height, e);
40460     },
40461
40462     // private
40463     updateChildSize : function(){
40464         if(this.resizeChild){
40465             var el = this.el;
40466             var child = this.resizeChild;
40467             var adj = this.adjustments;
40468             if(el.dom.offsetWidth){
40469                 var b = el.getSize(true);
40470                 child.setSize(b.width+adj[0], b.height+adj[1]);
40471             }
40472             // Second call here for IE
40473             // The first call enables instant resizing and
40474             // the second call corrects scroll bars if they
40475             // exist
40476             if(Ext.isIE){
40477                 setTimeout(function(){
40478                     if(el.dom.offsetWidth){
40479                         var b = el.getSize(true);
40480                         child.setSize(b.width+adj[0], b.height+adj[1]);
40481                     }
40482                 }, 10);
40483             }
40484         }
40485     },
40486
40487     // private
40488     snap : function(value, inc, min){
40489         if(!inc || !value){
40490             return value;
40491         }
40492         var newValue = value;
40493         var m = value % inc;
40494         if(m > 0){
40495             if(m > (inc/2)){
40496                 newValue = value + (inc-m);
40497             }else{
40498                 newValue = value - m;
40499             }
40500         }
40501         return Math.max(min, newValue);
40502     },
40503
40504     /**
40505      * <p>Performs resizing of the associated Element. This method is called internally by this
40506      * class, and should not be called by user code.</p>
40507      * <p>If a Resizable is being used to resize an Element which encapsulates a more complex UI
40508      * component such as a Panel, this method may be overridden by specifying an implementation
40509      * as a config option to provide appropriate behaviour at the end of the resize operation on
40510      * mouseup, for example resizing the Panel, and relaying the Panel's content.</p>
40511      * <p>The new area to be resized to is available by examining the state of the {@link #proxy}
40512      * Element. Example:
40513 <pre><code>
40514 new Ext.Panel({
40515     title: 'Resize me',
40516     x: 100,
40517     y: 100,
40518     renderTo: Ext.getBody(),
40519     floating: true,
40520     frame: true,
40521     width: 400,
40522     height: 200,
40523     listeners: {
40524         render: function(p) {
40525             new Ext.Resizable(p.getEl(), {
40526                 handles: 'all',
40527                 pinned: true,
40528                 transparent: true,
40529                 resizeElement: function() {
40530                     var box = this.proxy.getBox();
40531                     p.updateBox(box);
40532                     if (p.layout) {
40533                         p.doLayout();
40534                     }
40535                     return box;
40536                 }
40537            });
40538        }
40539     }
40540 }).show();
40541 </code></pre>
40542      */
40543     resizeElement : function(){
40544         var box = this.proxy.getBox();
40545         if(this.updateBox){
40546             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
40547         }else{
40548             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
40549         }
40550         this.updateChildSize();
40551         if(!this.dynamic){
40552             this.proxy.hide();
40553         }
40554         if(this.draggable && this.constrainTo){
40555             this.dd.resetConstraints();
40556             this.dd.constrainTo(this.constrainTo);
40557         }
40558         return box;
40559     },
40560
40561     // private
40562     constrain : function(v, diff, m, mx){
40563         if(v - diff < m){
40564             diff = v - m;
40565         }else if(v - diff > mx){
40566             diff = v - mx;
40567         }
40568         return diff;
40569     },
40570
40571     // private
40572     onMouseMove : function(e){
40573         if(this.enabled && this.activeHandle){
40574             try{// try catch so if something goes wrong the user doesn't get hung
40575
40576             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
40577                 return;
40578             }
40579
40580             //var curXY = this.startPoint;
40581             var curSize = this.curSize || this.startBox,
40582                 x = this.startBox.x, y = this.startBox.y,
40583                 ox = x,
40584                 oy = y,
40585                 w = curSize.width,
40586                 h = curSize.height,
40587                 ow = w,
40588                 oh = h,
40589                 mw = this.minWidth,
40590                 mh = this.minHeight,
40591                 mxw = this.maxWidth,
40592                 mxh = this.maxHeight,
40593                 wi = this.widthIncrement,
40594                 hi = this.heightIncrement,
40595                 eventXY = e.getXY(),
40596                 diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0])),
40597                 diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1])),
40598                 pos = this.activeHandle.position,
40599                 tw,
40600                 th;
40601
40602             switch(pos){
40603                 case 'east':
40604                     w += diffX;
40605                     w = Math.min(Math.max(mw, w), mxw);
40606                     break;
40607                 case 'south':
40608                     h += diffY;
40609                     h = Math.min(Math.max(mh, h), mxh);
40610                     break;
40611                 case 'southeast':
40612                     w += diffX;
40613                     h += diffY;
40614                     w = Math.min(Math.max(mw, w), mxw);
40615                     h = Math.min(Math.max(mh, h), mxh);
40616                     break;
40617                 case 'north':
40618                     diffY = this.constrain(h, diffY, mh, mxh);
40619                     y += diffY;
40620                     h -= diffY;
40621                     break;
40622                 case 'west':
40623                     diffX = this.constrain(w, diffX, mw, mxw);
40624                     x += diffX;
40625                     w -= diffX;
40626                     break;
40627                 case 'northeast':
40628                     w += diffX;
40629                     w = Math.min(Math.max(mw, w), mxw);
40630                     diffY = this.constrain(h, diffY, mh, mxh);
40631                     y += diffY;
40632                     h -= diffY;
40633                     break;
40634                 case 'northwest':
40635                     diffX = this.constrain(w, diffX, mw, mxw);
40636                     diffY = this.constrain(h, diffY, mh, mxh);
40637                     y += diffY;
40638                     h -= diffY;
40639                     x += diffX;
40640                     w -= diffX;
40641                     break;
40642                case 'southwest':
40643                     diffX = this.constrain(w, diffX, mw, mxw);
40644                     h += diffY;
40645                     h = Math.min(Math.max(mh, h), mxh);
40646                     x += diffX;
40647                     w -= diffX;
40648                     break;
40649             }
40650
40651             var sw = this.snap(w, wi, mw);
40652             var sh = this.snap(h, hi, mh);
40653             if(sw != w || sh != h){
40654                 switch(pos){
40655                     case 'northeast':
40656                         y -= sh - h;
40657                     break;
40658                     case 'north':
40659                         y -= sh - h;
40660                         break;
40661                     case 'southwest':
40662                         x -= sw - w;
40663                     break;
40664                     case 'west':
40665                         x -= sw - w;
40666                         break;
40667                     case 'northwest':
40668                         x -= sw - w;
40669                         y -= sh - h;
40670                     break;
40671                 }
40672                 w = sw;
40673                 h = sh;
40674             }
40675
40676             if(this.preserveRatio){
40677                 switch(pos){
40678                     case 'southeast':
40679                     case 'east':
40680                         h = oh * (w/ow);
40681                         h = Math.min(Math.max(mh, h), mxh);
40682                         w = ow * (h/oh);
40683                        break;
40684                     case 'south':
40685                         w = ow * (h/oh);
40686                         w = Math.min(Math.max(mw, w), mxw);
40687                         h = oh * (w/ow);
40688                         break;
40689                     case 'northeast':
40690                         w = ow * (h/oh);
40691                         w = Math.min(Math.max(mw, w), mxw);
40692                         h = oh * (w/ow);
40693                     break;
40694                     case 'north':
40695                         tw = w;
40696                         w = ow * (h/oh);
40697                         w = Math.min(Math.max(mw, w), mxw);
40698                         h = oh * (w/ow);
40699                         x += (tw - w) / 2;
40700                         break;
40701                     case 'southwest':
40702                         h = oh * (w/ow);
40703                         h = Math.min(Math.max(mh, h), mxh);
40704                         tw = w;
40705                         w = ow * (h/oh);
40706                         x += tw - w;
40707                         break;
40708                     case 'west':
40709                         th = h;
40710                         h = oh * (w/ow);
40711                         h = Math.min(Math.max(mh, h), mxh);
40712                         y += (th - h) / 2;
40713                         tw = w;
40714                         w = ow * (h/oh);
40715                         x += tw - w;
40716                        break;
40717                     case 'northwest':
40718                         tw = w;
40719                         th = h;
40720                         h = oh * (w/ow);
40721                         h = Math.min(Math.max(mh, h), mxh);
40722                         w = ow * (h/oh);
40723                         y += th - h;
40724                         x += tw - w;
40725                         break;
40726
40727                 }
40728             }
40729             this.proxy.setBounds(x, y, w, h);
40730             if(this.dynamic){
40731                 this.resizeElement();
40732             }
40733             }catch(ex){}
40734         }
40735     },
40736
40737     // private
40738     handleOver : function(){
40739         if(this.enabled){
40740             this.el.addClass('x-resizable-over');
40741         }
40742     },
40743
40744     // private
40745     handleOut : function(){
40746         if(!this.resizing){
40747             this.el.removeClass('x-resizable-over');
40748         }
40749     },
40750
40751     /**
40752      * Returns the element this component is bound to.
40753      * @return {Ext.Element}
40754      */
40755     getEl : function(){
40756         return this.el;
40757     },
40758
40759     /**
40760      * Returns the resizeChild element (or null).
40761      * @return {Ext.Element}
40762      */
40763     getResizeChild : function(){
40764         return this.resizeChild;
40765     },
40766
40767     /**
40768      * Destroys this resizable. If the element was wrapped and
40769      * removeEl is not true then the element remains.
40770      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
40771      */
40772     destroy : function(removeEl){
40773         Ext.destroy(this.dd, this.overlay, this.proxy);
40774         this.overlay = null;
40775         this.proxy = null;
40776
40777         var ps = Ext.Resizable.positions;
40778         for(var k in ps){
40779             if(typeof ps[k] != 'function' && this[ps[k]]){
40780                 this[ps[k]].destroy();
40781             }
40782         }
40783         if(removeEl){
40784             this.el.update('');
40785             Ext.destroy(this.el);
40786             this.el = null;
40787         }
40788         this.purgeListeners();
40789     },
40790
40791     syncHandleHeight : function(){
40792         var h = this.el.getHeight(true);
40793         if(this.west){
40794             this.west.el.setHeight(h);
40795         }
40796         if(this.east){
40797             this.east.el.setHeight(h);
40798         }
40799     }
40800 });
40801
40802 // private
40803 // hash to map config positions to true positions
40804 Ext.Resizable.positions = {
40805     n: 'north', s: 'south', e: 'east', w: 'west', se: 'southeast', sw: 'southwest', nw: 'northwest', ne: 'northeast'
40806 };
40807
40808 Ext.Resizable.Handle = Ext.extend(Object, {
40809     constructor : function(rz, pos, disableTrackOver, transparent, cls){
40810        if(!this.tpl){
40811             // only initialize the template if resizable is used
40812             var tpl = Ext.DomHelper.createTemplate(
40813                 {tag: 'div', cls: 'x-resizable-handle x-resizable-handle-{0}'}
40814             );
40815             tpl.compile();
40816             Ext.Resizable.Handle.prototype.tpl = tpl;
40817         }
40818         this.position = pos;
40819         this.rz = rz;
40820         this.el = this.tpl.append(rz.el.dom, [this.position], true);
40821         this.el.unselectable();
40822         if(transparent){
40823             this.el.setOpacity(0);
40824         }
40825         if(!Ext.isEmpty(cls)){
40826             this.el.addClass(cls);
40827         }
40828         this.el.on('mousedown', this.onMouseDown, this);
40829         if(!disableTrackOver){
40830             this.el.on({
40831                 scope: this,
40832                 mouseover: this.onMouseOver,
40833                 mouseout: this.onMouseOut
40834             });
40835         }
40836     },
40837
40838     // private
40839     afterResize : function(rz){
40840         // do nothing
40841     },
40842     // private
40843     onMouseDown : function(e){
40844         this.rz.onMouseDown(this, e);
40845     },
40846     // private
40847     onMouseOver : function(e){
40848         this.rz.handleOver(this, e);
40849     },
40850     // private
40851     onMouseOut : function(e){
40852         this.rz.handleOut(this, e);
40853     },
40854     // private
40855     destroy : function(){
40856         Ext.destroy(this.el);
40857         this.el = null;
40858     }
40859 });
40860 /**
40861  * @class Ext.Window
40862  * @extends Ext.Panel
40863  * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
40864  * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
40865  * restored to their prior size, and can be {@link #minimize}d.</p>
40866  * <p>Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} to provide
40867  * grouping, activation, to front, to back and other application-specific behavior.</p>
40868  * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
40869  * specify {@link Ext.Component#renderTo renderTo}.</p>
40870  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Window resulting in
40871  * destruction of any child Components. This makes the Window object, and all its descendants <b>unusable</b>. To enable
40872  * re-use of a Window, use <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
40873  * @constructor
40874  * @param {Object} config The config object
40875  * @xtype window
40876  */
40877 Ext.Window = Ext.extend(Ext.Panel, {
40878     /**
40879      * @cfg {Number} x
40880      * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
40881      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
40882      */
40883     /**
40884      * @cfg {Number} y
40885      * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
40886      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).
40887      */
40888     /**
40889      * @cfg {Boolean} modal
40890      * True to make the window modal and mask everything behind it when displayed, false to display it without
40891      * restricting access to other UI elements (defaults to false).
40892      */
40893     /**
40894      * @cfg {String/Element} animateTarget
40895      * Id or element from which the window should animate while opening (defaults to null with no animation).
40896      */
40897     /**
40898      * @cfg {String} resizeHandles
40899      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.
40900      */
40901     /**
40902      * @cfg {Ext.WindowGroup} manager
40903      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).
40904      */
40905     /**
40906     * @cfg {String/Number/Component} defaultButton
40907     * <p>Specifies a Component to receive focus when this Window is focussed.</p>
40908     * <p>This may be one of:</p><div class="mdetail-params"><ul>
40909     * <li>The index of a footer Button.</li>
40910     * <li>The id of a Component.</li>
40911     * <li>A Component.</li>
40912     * </ul></div>
40913     */
40914     /**
40915     * @cfg {Function} onEsc
40916     * Allows override of the built-in processing for the escape key. Default action
40917     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
40918     * To prevent the Window closing when the escape key is pressed, specify this as
40919     * Ext.emptyFn (See {@link Ext#emptyFn}).
40920     */
40921     /**
40922      * @cfg {Boolean} collapsed
40923      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
40924      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window
40925      * will always be expanded when shown.
40926      */
40927     /**
40928      * @cfg {Boolean} maximized
40929      * True to initially display the window in a maximized state. (Defaults to false).
40930      */
40931
40932     /**
40933     * @cfg {String} baseCls
40934     * The base CSS class to apply to this panel's element (defaults to 'x-window').
40935     */
40936     baseCls : 'x-window',
40937     /**
40938      * @cfg {Boolean} resizable
40939      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).
40940      */
40941     resizable : true,
40942     /**
40943      * @cfg {Boolean} draggable
40944      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
40945      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
40946      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).
40947      */
40948     draggable : true,
40949     /**
40950      * @cfg {Boolean} closable
40951      * <p>True to display the 'close' tool button and allow the user to close the window, false to
40952      * hide the button and disallow closing the window (defaults to true).</p>
40953      * <p>By default, when close is requested by either clicking the close button in the header
40954      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
40955      * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
40956      * it may not be reused.</p>
40957      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
40958      * {@link #closeAction} to 'hide'.
40959      */
40960     closable : true,
40961     /**
40962      * @cfg {String} closeAction
40963      * <p>The action to take when the close header tool is clicked:
40964      * <div class="mdetail-params"><ul>
40965      * <li><b><code>'{@link #close}'</code></b> : <b>Default</b><div class="sub-desc">
40966      * {@link #close remove} the window from the DOM and {@link Ext.Component#destroy destroy}
40967      * it and all descendant Components. The window will <b>not</b> be available to be
40968      * redisplayed via the {@link #show} method.
40969      * </div></li>
40970      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
40971      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
40972      * The window will be available to be redisplayed via the {@link #show} method.
40973      * </div></li>
40974      * </ul></div>
40975      * <p><b>Note:</b> This setting does not affect the {@link #close} method
40976      * which will always {@link Ext.Component#destroy destroy} the window. To
40977      * programatically <i>hide</i> a window, call {@link #hide}.</p>
40978      */
40979     closeAction : 'close',
40980     /**
40981      * @cfg {Boolean} constrain
40982      * True to constrain the window within its containing element, false to allow it to fall outside of its
40983      * containing element. By default the window will be rendered to document.body.  To render and constrain the
40984      * window within another element specify {@link #renderTo}.
40985      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
40986      */
40987     constrain : false,
40988     /**
40989      * @cfg {Boolean} constrainHeader
40990      * True to constrain the window header within its containing element (allowing the window body to fall outside
40991      * of its containing element) or false to allow the header to fall outside its containing element (defaults to
40992      * false). Optionally the entire window can be constrained using {@link #constrain}.
40993      */
40994     constrainHeader : false,
40995     /**
40996      * @cfg {Boolean} plain
40997      * True to render the window body with a transparent background so that it will blend into the framing
40998      * elements, false to add a lighter background color to visually highlight the body element and separate it
40999      * more distinctly from the surrounding frame (defaults to false).
41000      */
41001     plain : false,
41002     /**
41003      * @cfg {Boolean} minimizable
41004      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
41005      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
41006      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
41007      * custom minimize behavior implemented for this option to be useful.
41008      */
41009     minimizable : false,
41010     /**
41011      * @cfg {Boolean} maximizable
41012      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
41013      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
41014      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
41015      * restore the window to its previous size.
41016      */
41017     maximizable : false,
41018     /**
41019      * @cfg {Number} minHeight
41020      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.
41021      */
41022     minHeight : 100,
41023     /**
41024      * @cfg {Number} minWidth
41025      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.
41026      */
41027     minWidth : 200,
41028     /**
41029      * @cfg {Boolean} expandOnShow
41030      * True to always expand the window when it is displayed, false to keep it in its current state (which may be
41031      * {@link #collapsed}) when displayed (defaults to true).
41032      */
41033     expandOnShow : true,
41034
41035     // inherited docs, same default
41036     collapsible : false,
41037
41038     /**
41039      * @cfg {Boolean} initHidden
41040      * True to hide the window until show() is explicitly called (defaults to true).
41041      * @deprecated
41042      */
41043     initHidden : undefined,
41044
41045     /**
41046      * @cfg {Boolean} hidden
41047      * Render this component hidden (default is <tt>true</tt>). If <tt>true</tt>, the
41048      * {@link #hide} method will be called internally.
41049      */
41050     hidden : true,
41051
41052     // The following configs are set to provide the basic functionality of a window.
41053     // Changing them would require additional code to handle correctly and should
41054     // usually only be done in subclasses that can provide custom behavior.  Changing them
41055     // may have unexpected or undesirable results.
41056     /** @cfg {String} elements @hide */
41057     elements : 'header,body',
41058     /** @cfg {Boolean} frame @hide */
41059     frame : true,
41060     /** @cfg {Boolean} floating @hide */
41061     floating : true,
41062
41063     // private
41064     initComponent : function(){
41065         this.initTools();
41066         Ext.Window.superclass.initComponent.call(this);
41067         this.addEvents(
41068             /**
41069              * @event activate
41070              * Fires after the window has been visually activated via {@link #setActive}.
41071              * @param {Ext.Window} this
41072              */
41073             /**
41074              * @event deactivate
41075              * Fires after the window has been visually deactivated via {@link #setActive}.
41076              * @param {Ext.Window} this
41077              */
41078             /**
41079              * @event resize
41080              * Fires after the window has been resized.
41081              * @param {Ext.Window} this
41082              * @param {Number} width The window's new width
41083              * @param {Number} height The window's new height
41084              */
41085             'resize',
41086             /**
41087              * @event maximize
41088              * Fires after the window has been maximized.
41089              * @param {Ext.Window} this
41090              */
41091             'maximize',
41092             /**
41093              * @event minimize
41094              * Fires after the window has been minimized.
41095              * @param {Ext.Window} this
41096              */
41097             'minimize',
41098             /**
41099              * @event restore
41100              * Fires after the window has been restored to its original size after being maximized.
41101              * @param {Ext.Window} this
41102              */
41103             'restore'
41104         );
41105         // for backwards compat, this should be removed at some point
41106         if(Ext.isDefined(this.initHidden)){
41107             this.hidden = this.initHidden;
41108         }
41109         if(this.hidden === false){
41110             this.hidden = true;
41111             this.show();
41112         }
41113     },
41114
41115     // private
41116     getState : function(){
41117         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true));
41118     },
41119
41120     // private
41121     onRender : function(ct, position){
41122         Ext.Window.superclass.onRender.call(this, ct, position);
41123
41124         if(this.plain){
41125             this.el.addClass('x-window-plain');
41126         }
41127
41128         // this element allows the Window to be focused for keyboard events
41129         this.focusEl = this.el.createChild({
41130                     tag: 'a', href:'#', cls:'x-dlg-focus',
41131                     tabIndex:'-1', html: '&#160;'});
41132         this.focusEl.swallowEvent('click', true);
41133
41134         this.proxy = this.el.createProxy('x-window-proxy');
41135         this.proxy.enableDisplayMode('block');
41136
41137         if(this.modal){
41138             this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom);
41139             this.mask.enableDisplayMode('block');
41140             this.mask.hide();
41141             this.mon(this.mask, 'click', this.focus, this);
41142         }
41143         if(this.maximizable){
41144             this.mon(this.header, 'dblclick', this.toggleMaximize, this);
41145         }
41146     },
41147
41148     // private
41149     initEvents : function(){
41150         Ext.Window.superclass.initEvents.call(this);
41151         if(this.animateTarget){
41152             this.setAnimateTarget(this.animateTarget);
41153         }
41154
41155         if(this.resizable){
41156             this.resizer = new Ext.Resizable(this.el, {
41157                 minWidth: this.minWidth,
41158                 minHeight:this.minHeight,
41159                 handles: this.resizeHandles || 'all',
41160                 pinned: true,
41161                 resizeElement : this.resizerAction,
41162                 handleCls: 'x-window-handle'
41163             });
41164             this.resizer.window = this;
41165             this.mon(this.resizer, 'beforeresize', this.beforeResize, this);
41166         }
41167
41168         if(this.draggable){
41169             this.header.addClass('x-window-draggable');
41170         }
41171         this.mon(this.el, 'mousedown', this.toFront, this);
41172         this.manager = this.manager || Ext.WindowMgr;
41173         this.manager.register(this);
41174         if(this.maximized){
41175             this.maximized = false;
41176             this.maximize();
41177         }
41178         if(this.closable){
41179             var km = this.getKeyMap();
41180             km.on(27, this.onEsc, this);
41181             km.disable();
41182         }
41183     },
41184
41185     initDraggable : function(){
41186         /**
41187          * <p>If this Window is configured {@link #draggable}, this property will contain
41188          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.</p>
41189          * <p>This has implementations of <code>startDrag</code>, <code>onDrag</code> and <code>endDrag</code>
41190          * which perform the dragging action. If extra logic is needed at these points, use
41191          * {@link Function#createInterceptor createInterceptor} or {@link Function#createSequence createSequence} to
41192          * augment the existing implementations.</p>
41193          * @type Ext.dd.DD
41194          * @property dd
41195          */
41196         this.dd = new Ext.Window.DD(this);
41197     },
41198
41199    // private
41200     onEsc : function(k, e){
41201         e.stopEvent();
41202         this[this.closeAction]();
41203     },
41204
41205     // private
41206     beforeDestroy : function(){
41207         if(this.rendered){
41208             this.hide();
41209             this.clearAnchor();
41210             Ext.destroy(
41211                 this.focusEl,
41212                 this.resizer,
41213                 this.dd,
41214                 this.proxy,
41215                 this.mask
41216             );
41217         }
41218         Ext.Window.superclass.beforeDestroy.call(this);
41219     },
41220
41221     // private
41222     onDestroy : function(){
41223         if(this.manager){
41224             this.manager.unregister(this);
41225         }
41226         Ext.Window.superclass.onDestroy.call(this);
41227     },
41228
41229     // private
41230     initTools : function(){
41231         if(this.minimizable){
41232             this.addTool({
41233                 id: 'minimize',
41234                 handler: this.minimize.createDelegate(this, [])
41235             });
41236         }
41237         if(this.maximizable){
41238             this.addTool({
41239                 id: 'maximize',
41240                 handler: this.maximize.createDelegate(this, [])
41241             });
41242             this.addTool({
41243                 id: 'restore',
41244                 handler: this.restore.createDelegate(this, []),
41245                 hidden:true
41246             });
41247         }
41248         if(this.closable){
41249             this.addTool({
41250                 id: 'close',
41251                 handler: this[this.closeAction].createDelegate(this, [])
41252             });
41253         }
41254     },
41255
41256     // private
41257     resizerAction : function(){
41258         var box = this.proxy.getBox();
41259         this.proxy.hide();
41260         this.window.handleResize(box);
41261         return box;
41262     },
41263
41264     // private
41265     beforeResize : function(){
41266         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?
41267         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);
41268         this.resizeBox = this.el.getBox();
41269     },
41270
41271     // private
41272     updateHandles : function(){
41273         if(Ext.isIE && this.resizer){
41274             this.resizer.syncHandleHeight();
41275             this.el.repaint();
41276         }
41277     },
41278
41279     // private
41280     handleResize : function(box){
41281         var rz = this.resizeBox;
41282         if(rz.x != box.x || rz.y != box.y){
41283             this.updateBox(box);
41284         }else{
41285             this.setSize(box);
41286             if (Ext.isIE6 && Ext.isStrict) {
41287                 this.doLayout();
41288             }
41289         }
41290         this.focus();
41291         this.updateHandles();
41292         this.saveState();
41293     },
41294
41295     /**
41296      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the
41297      * window itself will receive focus.
41298      */
41299     focus : function(){
41300         var f = this.focusEl,
41301             db = this.defaultButton,
41302             t = typeof db,
41303             el,
41304             ct;
41305         if(Ext.isDefined(db)){
41306             if(Ext.isNumber(db) && this.fbar){
41307                 f = this.fbar.items.get(db);
41308             }else if(Ext.isString(db)){
41309                 f = Ext.getCmp(db);
41310             }else{
41311                 f = db;
41312             }
41313             el = f.getEl();
41314             ct = Ext.getDom(this.container);
41315             if (el && ct) {
41316                 if (!Ext.lib.Region.getRegion(ct).contains(Ext.lib.Region.getRegion(el.dom))){
41317                     return;
41318                 }
41319             }
41320         }
41321         f = f || this.focusEl;
41322         f.focus.defer(10, f);
41323     },
41324
41325     /**
41326      * Sets the target element from which the window should animate while opening.
41327      * @param {String/Element} el The target element or id
41328      */
41329     setAnimateTarget : function(el){
41330         el = Ext.get(el);
41331         this.animateTarget = el;
41332     },
41333
41334     // private
41335     beforeShow : function(){
41336         delete this.el.lastXY;
41337         delete this.el.lastLT;
41338         if(this.x === undefined || this.y === undefined){
41339             var xy = this.el.getAlignToXY(this.container, 'c-c');
41340             var pos = this.el.translatePoints(xy[0], xy[1]);
41341             this.x = this.x === undefined? pos.left : this.x;
41342             this.y = this.y === undefined? pos.top : this.y;
41343         }
41344         this.el.setLeftTop(this.x, this.y);
41345
41346         if(this.expandOnShow){
41347             this.expand(false);
41348         }
41349
41350         if(this.modal){
41351             Ext.getBody().addClass('x-body-masked');
41352             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
41353             this.mask.show();
41354         }
41355     },
41356
41357     /**
41358      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.
41359      * @param {String/Element} animateTarget (optional) The target element or id from which the window should
41360      * animate while opening (defaults to null with no animation)
41361      * @param {Function} callback (optional) A callback function to call after the window is displayed
41362      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
41363      * @return {Ext.Window} this
41364      */
41365     show : function(animateTarget, cb, scope){
41366         if(!this.rendered){
41367             this.render(Ext.getBody());
41368         }
41369         if(this.hidden === false){
41370             this.toFront();
41371             return this;
41372         }
41373         if(this.fireEvent('beforeshow', this) === false){
41374             return this;
41375         }
41376         if(cb){
41377             this.on('show', cb, scope, {single:true});
41378         }
41379         this.hidden = false;
41380         if(Ext.isDefined(animateTarget)){
41381             this.setAnimateTarget(animateTarget);
41382         }
41383         this.beforeShow();
41384         if(this.animateTarget){
41385             this.animShow();
41386         }else{
41387             this.afterShow();
41388         }
41389         return this;
41390     },
41391
41392     // private
41393     afterShow : function(isAnim){
41394         if (this.isDestroyed){
41395             return false;
41396         }
41397         this.proxy.hide();
41398         this.el.setStyle('display', 'block');
41399         this.el.show();
41400         if(this.maximized){
41401             this.fitContainer();
41402         }
41403         if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
41404             this.cascade(this.setAutoScroll);
41405         }
41406
41407         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
41408             Ext.EventManager.onWindowResize(this.onWindowResize, this);
41409         }
41410         this.doConstrain();
41411         this.doLayout();
41412         if(this.keyMap){
41413             this.keyMap.enable();
41414         }
41415         this.toFront();
41416         this.updateHandles();
41417         if(isAnim && (Ext.isIE || Ext.isWebKit)){
41418             var sz = this.getSize();
41419             this.onResize(sz.width, sz.height);
41420         }
41421         this.onShow();
41422         this.fireEvent('show', this);
41423     },
41424
41425     // private
41426     animShow : function(){
41427         this.proxy.show();
41428         this.proxy.setBox(this.animateTarget.getBox());
41429         this.proxy.setOpacity(0);
41430         var b = this.getBox();
41431         this.el.setStyle('display', 'none');
41432         this.proxy.shift(Ext.apply(b, {
41433             callback: this.afterShow.createDelegate(this, [true], false),
41434             scope: this,
41435             easing: 'easeNone',
41436             duration: 0.25,
41437             opacity: 0.5
41438         }));
41439     },
41440
41441     /**
41442      * Hides the window, setting it to invisible and applying negative offsets.
41443      * @param {String/Element} animateTarget (optional) The target element or id to which the window should
41444      * animate while hiding (defaults to null with no animation)
41445      * @param {Function} callback (optional) A callback function to call after the window is hidden
41446      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Window.
41447      * @return {Ext.Window} this
41448      */
41449     hide : function(animateTarget, cb, scope){
41450         if(this.hidden || this.fireEvent('beforehide', this) === false){
41451             return this;
41452         }
41453         if(cb){
41454             this.on('hide', cb, scope, {single:true});
41455         }
41456         this.hidden = true;
41457         if(animateTarget !== undefined){
41458             this.setAnimateTarget(animateTarget);
41459         }
41460         if(this.modal){
41461             this.mask.hide();
41462             Ext.getBody().removeClass('x-body-masked');
41463         }
41464         if(this.animateTarget){
41465             this.animHide();
41466         }else{
41467             this.el.hide();
41468             this.afterHide();
41469         }
41470         return this;
41471     },
41472
41473     // private
41474     afterHide : function(){
41475         this.proxy.hide();
41476         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
41477             Ext.EventManager.removeResizeListener(this.onWindowResize, this);
41478         }
41479         if(this.keyMap){
41480             this.keyMap.disable();
41481         }
41482         this.onHide();
41483         this.fireEvent('hide', this);
41484     },
41485
41486     // private
41487     animHide : function(){
41488         this.proxy.setOpacity(0.5);
41489         this.proxy.show();
41490         var tb = this.getBox(false);
41491         this.proxy.setBox(tb);
41492         this.el.hide();
41493         this.proxy.shift(Ext.apply(this.animateTarget.getBox(), {
41494             callback: this.afterHide,
41495             scope: this,
41496             duration: 0.25,
41497             easing: 'easeNone',
41498             opacity: 0
41499         }));
41500     },
41501
41502     /**
41503      * Method that is called immediately before the <code>show</code> event is fired.
41504      * Defaults to <code>Ext.emptyFn</code>.
41505      */
41506     onShow : Ext.emptyFn,
41507
41508     /**
41509      * Method that is called immediately before the <code>hide</code> event is fired.
41510      * Defaults to <code>Ext.emptyFn</code>.
41511      */
41512     onHide : Ext.emptyFn,
41513
41514     // private
41515     onWindowResize : function(){
41516         if(this.maximized){
41517             this.fitContainer();
41518         }
41519         if(this.modal){
41520             this.mask.setSize('100%', '100%');
41521             var force = this.mask.dom.offsetHeight;
41522             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
41523         }
41524         this.doConstrain();
41525     },
41526
41527     // private
41528     doConstrain : function(){
41529         if(this.constrain || this.constrainHeader){
41530             var offsets;
41531             if(this.constrain){
41532                 offsets = {
41533                     right:this.el.shadowOffset,
41534                     left:this.el.shadowOffset,
41535                     bottom:this.el.shadowOffset
41536                 };
41537             }else {
41538                 var s = this.getSize();
41539                 offsets = {
41540                     right:-(s.width - 100),
41541                     bottom:-(s.height - 25)
41542                 };
41543             }
41544
41545             var xy = this.el.getConstrainToXY(this.container, true, offsets);
41546             if(xy){
41547                 this.setPosition(xy[0], xy[1]);
41548             }
41549         }
41550     },
41551
41552     // private - used for dragging
41553     ghost : function(cls){
41554         var ghost = this.createGhost(cls);
41555         var box = this.getBox(true);
41556         ghost.setLeftTop(box.x, box.y);
41557         ghost.setWidth(box.width);
41558         this.el.hide();
41559         this.activeGhost = ghost;
41560         return ghost;
41561     },
41562
41563     // private
41564     unghost : function(show, matchPosition){
41565         if(!this.activeGhost) {
41566             return;
41567         }
41568         if(show !== false){
41569             this.el.show();
41570             this.focus.defer(10, this);
41571             if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
41572                 this.cascade(this.setAutoScroll);
41573             }
41574         }
41575         if(matchPosition !== false){
41576             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
41577         }
41578         this.activeGhost.hide();
41579         this.activeGhost.remove();
41580         delete this.activeGhost;
41581     },
41582
41583     /**
41584      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
41585      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
41586      * either the minimize event can be handled or this method can be overridden.
41587      * @return {Ext.Window} this
41588      */
41589     minimize : function(){
41590         this.fireEvent('minimize', this);
41591         return this;
41592     },
41593
41594     /**
41595      * <p>Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s
41596      * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose}
41597      * event is fired before the close happens and will cancel the close action if it returns false.<p>
41598      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
41599      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
41600      * To hide the Window without destroying it, call {@link #hide}.</p>
41601      */
41602     close : function(){
41603         if(this.fireEvent('beforeclose', this) !== false){
41604             if(this.hidden){
41605                 this.doClose();
41606             }else{
41607                 this.hide(null, this.doClose, this);
41608             }
41609         }
41610     },
41611
41612     // private
41613     doClose : function(){
41614         this.fireEvent('close', this);
41615         this.destroy();
41616     },
41617
41618     /**
41619      * Fits the window within its current container and automatically replaces
41620      * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
41621      * Also see {@link #toggleMaximize}.
41622      * @return {Ext.Window} this
41623      */
41624     maximize : function(){
41625         if(!this.maximized){
41626             this.expand(false);
41627             this.restoreSize = this.getSize();
41628             this.restorePos = this.getPosition(true);
41629             if (this.maximizable){
41630                 this.tools.maximize.hide();
41631                 this.tools.restore.show();
41632             }
41633             this.maximized = true;
41634             this.el.disableShadow();
41635
41636             if(this.dd){
41637                 this.dd.lock();
41638             }
41639             if(this.collapsible){
41640                 this.tools.toggle.hide();
41641             }
41642             this.el.addClass('x-window-maximized');
41643             this.container.addClass('x-window-maximized-ct');
41644
41645             this.setPosition(0, 0);
41646             this.fitContainer();
41647             this.fireEvent('maximize', this);
41648         }
41649         return this;
41650     },
41651
41652     /**
41653      * Restores a {@link #maximizable maximized}  window back to its original
41654      * size and position prior to being maximized and also replaces
41655      * the 'restore' tool button with the 'maximize' tool button.
41656      * Also see {@link #toggleMaximize}.
41657      * @return {Ext.Window} this
41658      */
41659     restore : function(){
41660         if(this.maximized){
41661             var t = this.tools;
41662             this.el.removeClass('x-window-maximized');
41663             if(t.restore){
41664                 t.restore.hide();
41665             }
41666             if(t.maximize){
41667                 t.maximize.show();
41668             }
41669             this.setPosition(this.restorePos[0], this.restorePos[1]);
41670             this.setSize(this.restoreSize.width, this.restoreSize.height);
41671             delete this.restorePos;
41672             delete this.restoreSize;
41673             this.maximized = false;
41674             this.el.enableShadow(true);
41675
41676             if(this.dd){
41677                 this.dd.unlock();
41678             }
41679             if(this.collapsible && t.toggle){
41680                 t.toggle.show();
41681             }
41682             this.container.removeClass('x-window-maximized-ct');
41683
41684             this.doConstrain();
41685             this.fireEvent('restore', this);
41686         }
41687         return this;
41688     },
41689
41690     /**
41691      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
41692      * state of the window.
41693      * @return {Ext.Window} this
41694      */
41695     toggleMaximize : function(){
41696         return this[this.maximized ? 'restore' : 'maximize']();
41697     },
41698
41699     // private
41700     fitContainer : function(){
41701         var vs = this.container.getViewSize(false);
41702         this.setSize(vs.width, vs.height);
41703     },
41704
41705     // private
41706     // z-index is managed by the WindowManager and may be overwritten at any time
41707     setZIndex : function(index){
41708         if(this.modal){
41709             this.mask.setStyle('z-index', index);
41710         }
41711         this.el.setZIndex(++index);
41712         index += 5;
41713
41714         if(this.resizer){
41715             this.resizer.proxy.setStyle('z-index', ++index);
41716         }
41717
41718         this.lastZIndex = index;
41719     },
41720
41721     /**
41722      * Aligns the window to the specified element
41723      * @param {Mixed} element The element to align to.
41724      * @param {String} position (optional, defaults to "tl-bl?") The position to align to (see {@link Ext.Element#alignTo} for more details).
41725      * @param {Array} offsets (optional) Offset the positioning by [x, y]
41726      * @return {Ext.Window} this
41727      */
41728     alignTo : function(element, position, offsets){
41729         var xy = this.el.getAlignToXY(element, position, offsets);
41730         this.setPagePosition(xy[0], xy[1]);
41731         return this;
41732     },
41733
41734     /**
41735      * Anchors this window to another element and realigns it when the window is resized or scrolled.
41736      * @param {Mixed} element The element to align to.
41737      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)
41738      * @param {Array} offsets (optional) Offset the positioning by [x, y]
41739      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
41740      * is a number, it is used as the buffer delay (defaults to 50ms).
41741      * @return {Ext.Window} this
41742      */
41743     anchorTo : function(el, alignment, offsets, monitorScroll){
41744         this.clearAnchor();
41745         this.anchorTarget = {
41746             el: el,
41747             alignment: alignment,
41748             offsets: offsets
41749         };
41750
41751         Ext.EventManager.onWindowResize(this.doAnchor, this);
41752         var tm = typeof monitorScroll;
41753         if(tm != 'undefined'){
41754             Ext.EventManager.on(window, 'scroll', this.doAnchor, this,
41755                 {buffer: tm == 'number' ? monitorScroll : 50});
41756         }
41757         return this.doAnchor();
41758     },
41759
41760     /**
41761      * Performs the anchor, using the saved anchorTarget property.
41762      * @return {Ext.Window} this
41763      * @private
41764      */
41765     doAnchor : function(){
41766         var o = this.anchorTarget;
41767         this.alignTo(o.el, o.alignment, o.offsets);
41768         return this;
41769     },
41770
41771     /**
41772      * Removes any existing anchor from this window. See {@link #anchorTo}.
41773      * @return {Ext.Window} this
41774      */
41775     clearAnchor : function(){
41776         if(this.anchorTarget){
41777             Ext.EventManager.removeResizeListener(this.doAnchor, this);
41778             Ext.EventManager.un(window, 'scroll', this.doAnchor, this);
41779             delete this.anchorTarget;
41780         }
41781         return this;
41782     },
41783
41784     /**
41785      * Brings this window to the front of any other visible windows
41786      * @param {Boolean} e (optional) Specify <tt>false</tt> to prevent the window from being focused.
41787      * @return {Ext.Window} this
41788      */
41789     toFront : function(e){
41790         if(this.manager.bringToFront(this)){
41791             if(!e || !e.getTarget().focus){
41792                 this.focus();
41793             }
41794         }
41795         return this;
41796     },
41797
41798     /**
41799      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also
41800      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred. This method is
41801      * called internally by {@link Ext.WindowMgr}.
41802      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)
41803      */
41804     setActive : function(active){
41805         if(active){
41806             if(!this.maximized){
41807                 this.el.enableShadow(true);
41808             }
41809             this.fireEvent('activate', this);
41810         }else{
41811             this.el.disableShadow();
41812             this.fireEvent('deactivate', this);
41813         }
41814     },
41815
41816     /**
41817      * Sends this window to the back of (lower z-index than) any other visible windows
41818      * @return {Ext.Window} this
41819      */
41820     toBack : function(){
41821         this.manager.sendToBack(this);
41822         return this;
41823     },
41824
41825     /**
41826      * Centers this window in the viewport
41827      * @return {Ext.Window} this
41828      */
41829     center : function(){
41830         var xy = this.el.getAlignToXY(this.container, 'c-c');
41831         this.setPagePosition(xy[0], xy[1]);
41832         return this;
41833     }
41834
41835     /**
41836      * @cfg {Boolean} autoWidth @hide
41837      **/
41838 });
41839 Ext.reg('window', Ext.Window);
41840
41841 // private - custom Window DD implementation
41842 Ext.Window.DD = function(win){
41843     this.win = win;
41844     Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);
41845     this.setHandleElId(win.header.id);
41846     this.scroll = false;
41847 };
41848
41849 Ext.extend(Ext.Window.DD, Ext.dd.DD, {
41850     moveOnly:true,
41851     headerOffsets:[100, 25],
41852     startDrag : function(){
41853         var w = this.win;
41854         this.proxy = w.ghost();
41855         if(w.constrain !== false){
41856             var so = w.el.shadowOffset;
41857             this.constrainTo(w.container, {right: so, left: so, bottom: so});
41858         }else if(w.constrainHeader !== false){
41859             var s = this.proxy.getSize();
41860             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});
41861         }
41862     },
41863     b4Drag : Ext.emptyFn,
41864
41865     onDrag : function(e){
41866         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());
41867     },
41868
41869     endDrag : function(e){
41870         this.win.unghost();
41871         this.win.saveState();
41872     }
41873 });
41874 /**
41875  * @class Ext.WindowGroup
41876  * An object that manages a group of {@link Ext.Window} instances and provides z-order management
41877  * and window activation behavior.
41878  * @constructor
41879  */
41880 Ext.WindowGroup = function(){
41881     var list = {};
41882     var accessList = [];
41883     var front = null;
41884
41885     // private
41886     var sortWindows = function(d1, d2){
41887         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
41888     };
41889
41890     // private
41891     var orderWindows = function(){
41892         var a = accessList, len = a.length;
41893         if(len > 0){
41894             a.sort(sortWindows);
41895             var seed = a[0].manager.zseed;
41896             for(var i = 0; i < len; i++){
41897                 var win = a[i];
41898                 if(win && !win.hidden){
41899                     win.setZIndex(seed + (i*10));
41900                 }
41901             }
41902         }
41903         activateLast();
41904     };
41905
41906     // private
41907     var setActiveWin = function(win){
41908         if(win != front){
41909             if(front){
41910                 front.setActive(false);
41911             }
41912             front = win;
41913             if(win){
41914                 win.setActive(true);
41915             }
41916         }
41917     };
41918
41919     // private
41920     var activateLast = function(){
41921         for(var i = accessList.length-1; i >=0; --i) {
41922             if(!accessList[i].hidden){
41923                 setActiveWin(accessList[i]);
41924                 return;
41925             }
41926         }
41927         // none to activate
41928         setActiveWin(null);
41929     };
41930
41931     return {
41932         /**
41933          * The starting z-index for windows in this WindowGroup (defaults to 9000)
41934          * @type Number The z-index value
41935          */
41936         zseed : 9000,
41937
41938         /**
41939          * <p>Registers a {@link Ext.Window Window} with this WindowManager. This should not
41940          * need to be called under normal circumstances. Windows are automatically registered
41941          * with a {@link Ext.Window#manager manager} at construction time.</p>
41942          * <p>Where this may be useful is moving Windows between two WindowManagers. For example,
41943          * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
41944          * WindowManager in the desktop sample app:</p><code><pre>
41945 var msgWin = Ext.MessageBox.getDialog();
41946 MyDesktop.getDesktop().getManager().register(msgWin);
41947 </pre></code>
41948          * @param {Window} win The Window to register.
41949          */
41950         register : function(win){
41951             if(win.manager){
41952                 win.manager.unregister(win);
41953             }
41954             win.manager = this;
41955
41956             list[win.id] = win;
41957             accessList.push(win);
41958             win.on('hide', activateLast);
41959         },
41960
41961         /**
41962          * <p>Unregisters a {@link Ext.Window Window} from this WindowManager. This should not
41963          * need to be called. Windows are automatically unregistered upon destruction.
41964          * See {@link #register}.</p>
41965          * @param {Window} win The Window to unregister.
41966          */
41967         unregister : function(win){
41968             delete win.manager;
41969             delete list[win.id];
41970             win.un('hide', activateLast);
41971             accessList.remove(win);
41972         },
41973
41974         /**
41975          * Gets a registered window by id.
41976          * @param {String/Object} id The id of the window or a {@link Ext.Window} instance
41977          * @return {Ext.Window}
41978          */
41979         get : function(id){
41980             return typeof id == "object" ? id : list[id];
41981         },
41982
41983         /**
41984          * Brings the specified window to the front of any other active windows in this WindowGroup.
41985          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
41986          * @return {Boolean} True if the dialog was brought to the front, else false
41987          * if it was already in front
41988          */
41989         bringToFront : function(win){
41990             win = this.get(win);
41991             if(win != front){
41992                 win._lastAccess = new Date().getTime();
41993                 orderWindows();
41994                 return true;
41995             }
41996             return false;
41997         },
41998
41999         /**
42000          * Sends the specified window to the back of other active windows in this WindowGroup.
42001          * @param {String/Object} win The id of the window or a {@link Ext.Window} instance
42002          * @return {Ext.Window} The window
42003          */
42004         sendToBack : function(win){
42005             win = this.get(win);
42006             win._lastAccess = -(new Date().getTime());
42007             orderWindows();
42008             return win;
42009         },
42010
42011         /**
42012          * Hides all windows in this WindowGroup.
42013          */
42014         hideAll : function(){
42015             for(var id in list){
42016                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
42017                     list[id].hide();
42018                 }
42019             }
42020         },
42021
42022         /**
42023          * Gets the currently-active window in this WindowGroup.
42024          * @return {Ext.Window} The active window
42025          */
42026         getActive : function(){
42027             return front;
42028         },
42029
42030         /**
42031          * Returns zero or more windows in this WindowGroup using the custom search function passed to this method.
42032          * The function should accept a single {@link Ext.Window} reference as its only argument and should
42033          * return true if the window matches the search criteria, otherwise it should return false.
42034          * @param {Function} fn The search function
42035          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Window being tested.
42036          * that gets passed to the function if not specified)
42037          * @return {Array} An array of zero or more matching windows
42038          */
42039         getBy : function(fn, scope){
42040             var r = [];
42041             for(var i = accessList.length-1; i >=0; --i) {
42042                 var win = accessList[i];
42043                 if(fn.call(scope||win, win) !== false){
42044                     r.push(win);
42045                 }
42046             }
42047             return r;
42048         },
42049
42050         /**
42051          * Executes the specified function once for every window in this WindowGroup, passing each
42052          * window as the only parameter. Returning false from the function will stop the iteration.
42053          * @param {Function} fn The function to execute for each item
42054          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Window in the iteration.
42055          */
42056         each : function(fn, scope){
42057             for(var id in list){
42058                 if(list[id] && typeof list[id] != "function"){
42059                     if(fn.call(scope || list[id], list[id]) === false){
42060                         return;
42061                     }
42062                 }
42063             }
42064         }
42065     };
42066 };
42067
42068
42069 /**
42070  * @class Ext.WindowMgr
42071  * @extends Ext.WindowGroup
42072  * The default global window group that is available automatically.  To have more than one group of windows
42073  * with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
42074  * @singleton
42075  */
42076 Ext.WindowMgr = new Ext.WindowGroup();/**
42077  * @class Ext.MessageBox
42078  * <p>Utility class for generating different styles of message boxes.  The alias Ext.Msg can also be used.<p/>
42079  * <p>Note that the MessageBox is asynchronous.  Unlike a regular JavaScript <code>alert</code> (which will halt
42080  * browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code
42081  * that should only run <em>after</em> some user feedback from the MessageBox, you must use a callback function
42082  * (see the <code>function</code> parameter for {@link #show} for more details).</p>
42083  * <p>Example usage:</p>
42084  *<pre><code>
42085 // Basic alert:
42086 Ext.Msg.alert('Status', 'Changes saved successfully.');
42087
42088 // Prompt for user data and process the result using a callback:
42089 Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
42090     if (btn == 'ok'){
42091         // process text value and close...
42092     }
42093 });
42094
42095 // Show a dialog using config options:
42096 Ext.Msg.show({
42097    title:'Save Changes?',
42098    msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',
42099    buttons: Ext.Msg.YESNOCANCEL,
42100    fn: processResult,
42101    animEl: 'elId',
42102    icon: Ext.MessageBox.QUESTION
42103 });
42104 </code></pre>
42105  * @singleton
42106  */
42107 Ext.MessageBox = function(){
42108     var dlg, opt, mask, waitTimer,
42109         bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl,
42110         buttons, activeTextEl, bwidth, bufferIcon = '', iconCls = '',
42111         buttonNames = ['ok', 'yes', 'no', 'cancel'];
42112
42113     // private
42114     var handleButton = function(button){
42115         buttons[button].blur();
42116         if(dlg.isVisible()){
42117             dlg.hide();
42118             handleHide();
42119             Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);
42120         }
42121     };
42122
42123     // private
42124     var handleHide = function(){
42125         if(opt && opt.cls){
42126             dlg.el.removeClass(opt.cls);
42127         }
42128         progressBar.reset();        
42129     };
42130
42131     // private
42132     var handleEsc = function(d, k, e){
42133         if(opt && opt.closable !== false){
42134             dlg.hide();
42135             handleHide();
42136         }
42137         if(e){
42138             e.stopEvent();
42139         }
42140     };
42141
42142     // private
42143     var updateButtons = function(b){
42144         var width = 0,
42145             cfg;
42146         if(!b){
42147             Ext.each(buttonNames, function(name){
42148                 buttons[name].hide();
42149             });
42150             return width;
42151         }
42152         dlg.footer.dom.style.display = '';
42153         Ext.iterate(buttons, function(name, btn){
42154             cfg = b[name];
42155             if(cfg){
42156                 btn.show();
42157                 btn.setText(Ext.isString(cfg) ? cfg : Ext.MessageBox.buttonText[name]);
42158                 width += btn.getEl().getWidth() + 15;
42159             }else{
42160                 btn.hide();
42161             }
42162         });
42163         return width;
42164     };
42165
42166     return {
42167         /**
42168          * Returns a reference to the underlying {@link Ext.Window} element
42169          * @return {Ext.Window} The window
42170          */
42171         getDialog : function(titleText){
42172            if(!dlg){
42173                 var btns = [];
42174                 
42175                 buttons = {};
42176                 Ext.each(buttonNames, function(name){
42177                     btns.push(buttons[name] = new Ext.Button({
42178                         text: this.buttonText[name],
42179                         handler: handleButton.createCallback(name),
42180                         hideMode: 'offsets'
42181                     }));
42182                 }, this);
42183                 dlg = new Ext.Window({
42184                     autoCreate : true,
42185                     title:titleText,
42186                     resizable:false,
42187                     constrain:true,
42188                     constrainHeader:true,
42189                     minimizable : false,
42190                     maximizable : false,
42191                     stateful: false,
42192                     modal: true,
42193                     shim:true,
42194                     buttonAlign:"center",
42195                     width:400,
42196                     height:100,
42197                     minHeight: 80,
42198                     plain:true,
42199                     footer:true,
42200                     closable:true,
42201                     close : function(){
42202                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
42203                             handleButton("no");
42204                         }else{
42205                             handleButton("cancel");
42206                         }
42207                     },
42208                     fbar: new Ext.Toolbar({
42209                         items: btns,
42210                         enableOverflow: false
42211                     })
42212                 });
42213                 dlg.render(document.body);
42214                 dlg.getEl().addClass('x-window-dlg');
42215                 mask = dlg.mask;
42216                 bodyEl = dlg.body.createChild({
42217                     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>'
42218                 });
42219                 iconEl = Ext.get(bodyEl.dom.firstChild);
42220                 var contentEl = bodyEl.dom.childNodes[1];
42221                 msgEl = Ext.get(contentEl.firstChild);
42222                 textboxEl = Ext.get(contentEl.childNodes[2].firstChild);
42223                 textboxEl.enableDisplayMode();
42224                 textboxEl.addKeyListener([10,13], function(){
42225                     if(dlg.isVisible() && opt && opt.buttons){
42226                         if(opt.buttons.ok){
42227                             handleButton("ok");
42228                         }else if(opt.buttons.yes){
42229                             handleButton("yes");
42230                         }
42231                     }
42232                 });
42233                 textareaEl = Ext.get(contentEl.childNodes[2].childNodes[1]);
42234                 textareaEl.enableDisplayMode();
42235                 progressBar = new Ext.ProgressBar({
42236                     renderTo:bodyEl
42237                 });
42238                bodyEl.createChild({cls:'x-clear'});
42239             }
42240             return dlg;
42241         },
42242
42243         /**
42244          * Updates the message box body text
42245          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
42246          * the XHTML-compliant non-breaking space character '&amp;#160;')
42247          * @return {Ext.MessageBox} this
42248          */
42249         updateText : function(text){
42250             if(!dlg.isVisible() && !opt.width){
42251                 dlg.setSize(this.maxWidth, 100); // resize first so content is never clipped from previous shows
42252             }
42253             msgEl.update(text || '&#160;');
42254
42255             var iw = iconCls != '' ? (iconEl.getWidth() + iconEl.getMargins('lr')) : 0,
42256                 mw = msgEl.getWidth() + msgEl.getMargins('lr'),
42257                 fw = dlg.getFrameWidth('lr'),
42258                 bw = dlg.body.getFrameWidth('lr'),
42259                 w;
42260                 
42261             if (Ext.isIE && iw > 0){
42262                 //3 pixels get subtracted in the icon CSS for an IE margin issue,
42263                 //so we have to add it back here for the overall width to be consistent
42264                 iw += 3;
42265             }
42266             w = Math.max(Math.min(opt.width || iw+mw+fw+bw, opt.maxWidth || this.maxWidth),
42267                     Math.max(opt.minWidth || this.minWidth, bwidth || 0));
42268
42269             if(opt.prompt === true){
42270                 activeTextEl.setWidth(w-iw-fw-bw);
42271             }
42272             if(opt.progress === true || opt.wait === true){
42273                 progressBar.setSize(w-iw-fw-bw);
42274             }
42275             if(Ext.isIE && w == bwidth){
42276                 w += 4; //Add offset when the content width is smaller than the buttons.    
42277             }
42278             dlg.setSize(w, 'auto').center();
42279             return this;
42280         },
42281
42282         /**
42283          * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
42284          * initiated via {@link Ext.MessageBox#progress} or {@link Ext.MessageBox#wait},
42285          * or by calling {@link Ext.MessageBox#show} with progress: true.
42286          * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)
42287          * @param {String} progressText The progress text to display inside the progress bar (defaults to '')
42288          * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined
42289          * so that any existing body text will not get overwritten by default unless a new value is passed in)
42290          * @return {Ext.MessageBox} this
42291          */
42292         updateProgress : function(value, progressText, msg){
42293             progressBar.updateProgress(value, progressText);
42294             if(msg){
42295                 this.updateText(msg);
42296             }
42297             return this;
42298         },
42299
42300         /**
42301          * Returns true if the message box is currently displayed
42302          * @return {Boolean} True if the message box is visible, else false
42303          */
42304         isVisible : function(){
42305             return dlg && dlg.isVisible();
42306         },
42307
42308         /**
42309          * Hides the message box if it is displayed
42310          * @return {Ext.MessageBox} this
42311          */
42312         hide : function(){
42313             var proxy = dlg ? dlg.activeGhost : null;
42314             if(this.isVisible() || proxy){
42315                 dlg.hide();
42316                 handleHide();
42317                 if (proxy){
42318                     // unghost is a private function, but i saw no better solution
42319                     // to fix the locking problem when dragging while it closes
42320                     dlg.unghost(false, false);
42321                 } 
42322             }
42323             return this;
42324         },
42325
42326         /**
42327          * Displays a new message box, or reinitializes an existing message box, based on the config options
42328          * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,
42329          * although those calls are basic shortcuts and do not support all of the config options allowed here.
42330          * @param {Object} config The following config options are supported: <ul>
42331          * <li><b>animEl</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it
42332          * opens and closes (defaults to undefined)</div></li>
42333          * <li><b>buttons</b> : Object/Boolean<div class="sub-desc">A button config object (e.g., Ext.MessageBox.OKCANCEL or {ok:'Foo',
42334          * cancel:'Bar'}), or false to not show any buttons (defaults to false)</div></li>
42335          * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that
42336          * progress and wait dialogs will ignore this property and always hide the close button as they can only
42337          * be closed programmatically.</div></li>
42338          * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>
42339          * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea
42340          * if displayed (defaults to 75)</div></li>
42341          * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either
42342          * by clicking on the configured buttons, or on the dialog close button, or by pressing
42343          * the return button to enter input.
42344          * <p>Progress and wait dialogs will ignore this option since they do not respond to user
42345          * actions and can only be closed programmatically, so any required function should be called
42346          * by the same code after it closes the dialog. Parameters passed:<ul>
42347          * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>
42348          * <li><tt>ok</tt></li>
42349          * <li><tt>yes</tt></li>
42350          * <li><tt>no</tt></li>
42351          * <li><tt>cancel</tt></li>
42352          * </ul></div></div></li>
42353          * <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>
42354          * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.MessageBox">multiline</a></tt> is true</div></li>
42355          * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>
42356          * </ul></p></div></li>
42357          * <li><b>scope</b> : Object<div class="sub-desc">The scope of the callback function</div></li>
42358          * <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
42359          * dialog (e.g. Ext.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>
42360          * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.Window#iconCls} to
42361          * add an optional header icon (defaults to '')</div></li>
42362          * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>
42363          * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>
42364          * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is
42365          * displayed (defaults to true)</div></li>
42366          * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the
42367          * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>
42368          * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">
42369          * True to prompt the user to enter multi-line text (defaults to false)</div></li>
42370          * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
42371          * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>
42372          * <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>
42373          * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>
42374          * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>
42375          * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>
42376          * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
42377          * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>
42378          * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>
42379          * </ul>
42380          * Example usage:
42381          * <pre><code>
42382 Ext.Msg.show({
42383    title: 'Address',
42384    msg: 'Please enter your address:',
42385    width: 300,
42386    buttons: Ext.MessageBox.OKCANCEL,
42387    multiline: true,
42388    fn: saveAddress,
42389    animEl: 'addAddressBtn',
42390    icon: Ext.MessageBox.INFO
42391 });
42392 </code></pre>
42393          * @return {Ext.MessageBox} this
42394          */
42395         show : function(options){
42396             if(this.isVisible()){
42397                 this.hide();
42398             }
42399             opt = options;
42400             var d = this.getDialog(opt.title || "&#160;");
42401
42402             d.setTitle(opt.title || "&#160;");
42403             var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);
42404             d.tools.close.setDisplayed(allowClose);
42405             activeTextEl = textboxEl;
42406             opt.prompt = opt.prompt || (opt.multiline ? true : false);
42407             if(opt.prompt){
42408                 if(opt.multiline){
42409                     textboxEl.hide();
42410                     textareaEl.show();
42411                     textareaEl.setHeight(Ext.isNumber(opt.multiline) ? opt.multiline : this.defaultTextHeight);
42412                     activeTextEl = textareaEl;
42413                 }else{
42414                     textboxEl.show();
42415                     textareaEl.hide();
42416                 }
42417             }else{
42418                 textboxEl.hide();
42419                 textareaEl.hide();
42420             }
42421             activeTextEl.dom.value = opt.value || "";
42422             if(opt.prompt){
42423                 d.focusEl = activeTextEl;
42424             }else{
42425                 var bs = opt.buttons;
42426                 var db = null;
42427                 if(bs && bs.ok){
42428                     db = buttons["ok"];
42429                 }else if(bs && bs.yes){
42430                     db = buttons["yes"];
42431                 }
42432                 if (db){
42433                     d.focusEl = db;
42434                 }
42435             }
42436             if(opt.iconCls){
42437               d.setIconClass(opt.iconCls);
42438             }
42439             this.setIcon(Ext.isDefined(opt.icon) ? opt.icon : bufferIcon);
42440             bwidth = updateButtons(opt.buttons);
42441             progressBar.setVisible(opt.progress === true || opt.wait === true);
42442             this.updateProgress(0, opt.progressText);
42443             this.updateText(opt.msg);
42444             if(opt.cls){
42445                 d.el.addClass(opt.cls);
42446             }
42447             d.proxyDrag = opt.proxyDrag === true;
42448             d.modal = opt.modal !== false;
42449             d.mask = opt.modal !== false ? mask : false;
42450             if(!d.isVisible()){
42451                 // force it to the end of the z-index stack so it gets a cursor in FF
42452                 document.body.appendChild(dlg.el.dom);
42453                 d.setAnimateTarget(opt.animEl);
42454                 //workaround for window internally enabling keymap in afterShow
42455                 d.on('show', function(){
42456                     if(allowClose === true){
42457                         d.keyMap.enable();
42458                     }else{
42459                         d.keyMap.disable();
42460                     }
42461                 }, this, {single:true});
42462                 d.show(opt.animEl);
42463             }
42464             if(opt.wait === true){
42465                 progressBar.wait(opt.waitConfig);
42466             }
42467             return this;
42468         },
42469
42470         /**
42471          * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default
42472          * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
42473          * to clear any existing icon. This method must be called before the MessageBox is shown.
42474          * The following built-in icon classes are supported, but you can also pass in a custom class name:
42475          * <pre>
42476 Ext.MessageBox.INFO
42477 Ext.MessageBox.WARNING
42478 Ext.MessageBox.QUESTION
42479 Ext.MessageBox.ERROR
42480          *</pre>
42481          * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon
42482          * @return {Ext.MessageBox} this
42483          */
42484         setIcon : function(icon){
42485             if(!dlg){
42486                 bufferIcon = icon;
42487                 return;
42488             }
42489             bufferIcon = undefined;
42490             if(icon && icon != ''){
42491                 iconEl.removeClass('x-hidden');
42492                 iconEl.replaceClass(iconCls, icon);
42493                 bodyEl.addClass('x-dlg-icon');
42494                 iconCls = icon;
42495             }else{
42496                 iconEl.replaceClass(iconCls, 'x-hidden');
42497                 bodyEl.removeClass('x-dlg-icon');
42498                 iconCls = '';
42499             }
42500             return this;
42501         },
42502
42503         /**
42504          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
42505          * the user.  You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress}
42506          * and closing the message box when the process is complete.
42507          * @param {String} title The title bar text
42508          * @param {String} msg The message box body text
42509          * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')
42510          * @return {Ext.MessageBox} this
42511          */
42512         progress : function(title, msg, progressText){
42513             this.show({
42514                 title : title,
42515                 msg : msg,
42516                 buttons: false,
42517                 progress:true,
42518                 closable:false,
42519                 minWidth: this.minProgressWidth,
42520                 progressText: progressText
42521             });
42522             return this;
42523         },
42524
42525         /**
42526          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
42527          * interaction while waiting for a long-running process to complete that does not have defined intervals.
42528          * You are responsible for closing the message box when the process is complete.
42529          * @param {String} msg The message box body text
42530          * @param {String} title (optional) The title bar text
42531          * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object
42532          * @return {Ext.MessageBox} this
42533          */
42534         wait : function(msg, title, config){
42535             this.show({
42536                 title : title,
42537                 msg : msg,
42538                 buttons: false,
42539                 closable:false,
42540                 wait:true,
42541                 modal:true,
42542                 minWidth: this.minProgressWidth,
42543                 waitConfig: config
42544             });
42545             return this;
42546         },
42547
42548         /**
42549          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).
42550          * If a callback function is passed it will be called after the user clicks the button, and the
42551          * id of the button that was clicked will be passed as the only parameter to the callback
42552          * (could also be the top-right close button).
42553          * @param {String} title The title bar text
42554          * @param {String} msg The message box body text
42555          * @param {Function} fn (optional) The callback function invoked after the message box is closed
42556          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
42557          * @return {Ext.MessageBox} this
42558          */
42559         alert : function(title, msg, fn, scope){
42560             this.show({
42561                 title : title,
42562                 msg : msg,
42563                 buttons: this.OK,
42564                 fn: fn,
42565                 scope : scope,
42566                 minWidth: this.minWidth
42567             });
42568             return this;
42569         },
42570
42571         /**
42572          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
42573          * If a callback function is passed it will be called after the user clicks either button,
42574          * and the id of the button that was clicked will be passed as the only parameter to the callback
42575          * (could also be the top-right close button).
42576          * @param {String} title The title bar text
42577          * @param {String} msg The message box body text
42578          * @param {Function} fn (optional) The callback function invoked after the message box is closed
42579          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
42580          * @return {Ext.MessageBox} this
42581          */
42582         confirm : function(title, msg, fn, scope){
42583             this.show({
42584                 title : title,
42585                 msg : msg,
42586                 buttons: this.YESNO,
42587                 fn: fn,
42588                 scope : scope,
42589                 icon: this.QUESTION,
42590                 minWidth: this.minWidth
42591             });
42592             return this;
42593         },
42594
42595         /**
42596          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
42597          * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user
42598          * clicks either button, and the id of the button that was clicked (could also be the top-right
42599          * close button) and the text that was entered will be passed as the two parameters to the callback.
42600          * @param {String} title The title bar text
42601          * @param {String} msg The message box body text
42602          * @param {Function} fn (optional) The callback function invoked after the message box is closed
42603          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
42604          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
42605          * property, or the height in pixels to create the textbox (defaults to false / single-line)
42606          * @param {String} value (optional) Default value of the text input element (defaults to '')
42607          * @return {Ext.MessageBox} this
42608          */
42609         prompt : function(title, msg, fn, scope, multiline, value){
42610             this.show({
42611                 title : title,
42612                 msg : msg,
42613                 buttons: this.OKCANCEL,
42614                 fn: fn,
42615                 minWidth: this.minPromptWidth,
42616                 scope : scope,
42617                 prompt:true,
42618                 multiline: multiline,
42619                 value: value
42620             });
42621             return this;
42622         },
42623
42624         /**
42625          * Button config that displays a single OK button
42626          * @type Object
42627          */
42628         OK : {ok:true},
42629         /**
42630          * Button config that displays a single Cancel button
42631          * @type Object
42632          */
42633         CANCEL : {cancel:true},
42634         /**
42635          * Button config that displays OK and Cancel buttons
42636          * @type Object
42637          */
42638         OKCANCEL : {ok:true, cancel:true},
42639         /**
42640          * Button config that displays Yes and No buttons
42641          * @type Object
42642          */
42643         YESNO : {yes:true, no:true},
42644         /**
42645          * Button config that displays Yes, No and Cancel buttons
42646          * @type Object
42647          */
42648         YESNOCANCEL : {yes:true, no:true, cancel:true},
42649         /**
42650          * The CSS class that provides the INFO icon image
42651          * @type String
42652          */
42653         INFO : 'ext-mb-info',
42654         /**
42655          * The CSS class that provides the WARNING icon image
42656          * @type String
42657          */
42658         WARNING : 'ext-mb-warning',
42659         /**
42660          * The CSS class that provides the QUESTION icon image
42661          * @type String
42662          */
42663         QUESTION : 'ext-mb-question',
42664         /**
42665          * The CSS class that provides the ERROR icon image
42666          * @type String
42667          */
42668         ERROR : 'ext-mb-error',
42669
42670         /**
42671          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
42672          * @type Number
42673          */
42674         defaultTextHeight : 75,
42675         /**
42676          * The maximum width in pixels of the message box (defaults to 600)
42677          * @type Number
42678          */
42679         maxWidth : 600,
42680         /**
42681          * The minimum width in pixels of the message box (defaults to 100)
42682          * @type Number
42683          */
42684         minWidth : 100,
42685         /**
42686          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
42687          * for setting a different minimum width than text-only dialogs may need (defaults to 250).
42688          * @type Number
42689          */
42690         minProgressWidth : 250,
42691         /**
42692          * The minimum width in pixels of the message box if it is a prompt dialog.  This is useful
42693          * for setting a different minimum width than text-only dialogs may need (defaults to 250).
42694          * @type Number
42695          */
42696         minPromptWidth: 250,
42697         /**
42698          * An object containing the default button text strings that can be overriden for localized language support.
42699          * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific
42700          * resource file for handling language support across the framework.
42701          * Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french
42702          * @type Object
42703          */
42704         buttonText : {
42705             ok : "OK",
42706             cancel : "Cancel",
42707             yes : "Yes",
42708             no : "No"
42709         }
42710     };
42711 }();
42712
42713 /**
42714  * Shorthand for {@link Ext.MessageBox}
42715  */
42716 Ext.Msg = Ext.MessageBox;/**
42717  * @class Ext.dd.PanelProxy
42718  * A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally
42719  * for the Panel's drag drop implementation, and should never need to be created directly.
42720  * @constructor
42721  * @param panel The {@link Ext.Panel} to proxy for
42722  * @param config Configuration options
42723  */
42724 Ext.dd.PanelProxy = function(panel, config){
42725     this.panel = panel;
42726     this.id = this.panel.id +'-ddproxy';
42727     Ext.apply(this, config);
42728 };
42729
42730 Ext.dd.PanelProxy.prototype = {
42731     /**
42732      * @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,
42733      * false to drag with no proxy (defaults to true).
42734      */
42735     insertProxy : true,
42736
42737     // private overrides
42738     setStatus : Ext.emptyFn,
42739     reset : Ext.emptyFn,
42740     update : Ext.emptyFn,
42741     stop : Ext.emptyFn,
42742     sync: Ext.emptyFn,
42743
42744     /**
42745      * Gets the proxy's element
42746      * @return {Element} The proxy's element
42747      */
42748     getEl : function(){
42749         return this.ghost;
42750     },
42751
42752     /**
42753      * Gets the proxy's ghost element
42754      * @return {Element} The proxy's ghost element
42755      */
42756     getGhost : function(){
42757         return this.ghost;
42758     },
42759
42760     /**
42761      * Gets the proxy's element
42762      * @return {Element} The proxy's element
42763      */
42764     getProxy : function(){
42765         return this.proxy;
42766     },
42767
42768     /**
42769      * Hides the proxy
42770      */
42771     hide : function(){
42772         if(this.ghost){
42773             if(this.proxy){
42774                 this.proxy.remove();
42775                 delete this.proxy;
42776             }
42777             this.panel.el.dom.style.display = '';
42778             this.ghost.remove();
42779             delete this.ghost;
42780         }
42781     },
42782
42783     /**
42784      * Shows the proxy
42785      */
42786     show : function(){
42787         if(!this.ghost){
42788             this.ghost = this.panel.createGhost(undefined, undefined, Ext.getBody());
42789             this.ghost.setXY(this.panel.el.getXY());
42790             if(this.insertProxy){
42791                 this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});
42792                 this.proxy.setSize(this.panel.getSize());
42793             }
42794             this.panel.el.dom.style.display = 'none';
42795         }
42796     },
42797
42798     // private
42799     repair : function(xy, callback, scope){
42800         this.hide();
42801         if(typeof callback == "function"){
42802             callback.call(scope || this);
42803         }
42804     },
42805
42806     /**
42807      * Moves the proxy to a different position in the DOM.  This is typically called while dragging the Panel
42808      * to keep the proxy sync'd to the Panel's location.
42809      * @param {HTMLElement} parentNode The proxy's parent DOM node
42810      * @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults
42811      * to the parent's last child if not specified)
42812      */
42813     moveProxy : function(parentNode, before){
42814         if(this.proxy){
42815             parentNode.insertBefore(this.proxy.dom, before);
42816         }
42817     }
42818 };
42819
42820 // private - DD implementation for Panels
42821 Ext.Panel.DD = function(panel, cfg){
42822     this.panel = panel;
42823     this.dragData = {panel: panel};
42824     this.proxy = new Ext.dd.PanelProxy(panel, cfg);
42825     Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);
42826     var h = panel.header;
42827     if(h){
42828         this.setHandleElId(h.id);
42829     }
42830     (h ? h : this.panel.body).setStyle('cursor', 'move');
42831     this.scroll = false;
42832 };
42833
42834 Ext.extend(Ext.Panel.DD, Ext.dd.DragSource, {
42835     showFrame: Ext.emptyFn,
42836     startDrag: Ext.emptyFn,
42837     b4StartDrag: function(x, y) {
42838         this.proxy.show();
42839     },
42840     b4MouseDown: function(e) {
42841         var x = e.getPageX();
42842         var y = e.getPageY();
42843         this.autoOffset(x, y);
42844     },
42845     onInitDrag : function(x, y){
42846         this.onStartDrag(x, y);
42847         return true;
42848     },
42849     createFrame : Ext.emptyFn,
42850     getDragEl : function(e){
42851         return this.proxy.ghost.dom;
42852     },
42853     endDrag : function(e){
42854         this.proxy.hide();
42855         this.panel.saveState();
42856     },
42857
42858     autoOffset : function(x, y) {
42859         x -= this.startPageX;
42860         y -= this.startPageY;
42861         this.setDelta(x, y);
42862     }
42863 });/**
42864  * @class Ext.state.Provider
42865  * Abstract base class for state provider implementations. This class provides methods
42866  * for encoding and decoding <b>typed</b> variables including dates and defines the
42867  * Provider interface.
42868  */
42869 Ext.state.Provider = function(){
42870     /**
42871      * @event statechange
42872      * Fires when a state change occurs.
42873      * @param {Provider} this This state provider
42874      * @param {String} key The state key which was changed
42875      * @param {String} value The encoded value for the state
42876      */
42877     this.addEvents("statechange");
42878     this.state = {};
42879     Ext.state.Provider.superclass.constructor.call(this);
42880 };
42881 Ext.extend(Ext.state.Provider, Ext.util.Observable, {
42882     /**
42883      * Returns the current value for a key
42884      * @param {String} name The key name
42885      * @param {Mixed} defaultValue A default value to return if the key's value is not found
42886      * @return {Mixed} The state data
42887      */
42888     get : function(name, defaultValue){
42889         return typeof this.state[name] == "undefined" ?
42890             defaultValue : this.state[name];
42891     },
42892
42893     /**
42894      * Clears a value from the state
42895      * @param {String} name The key name
42896      */
42897     clear : function(name){
42898         delete this.state[name];
42899         this.fireEvent("statechange", this, name, null);
42900     },
42901
42902     /**
42903      * Sets the value for a key
42904      * @param {String} name The key name
42905      * @param {Mixed} value The value to set
42906      */
42907     set : function(name, value){
42908         this.state[name] = value;
42909         this.fireEvent("statechange", this, name, value);
42910     },
42911
42912     /**
42913      * Decodes a string previously encoded with {@link #encodeValue}.
42914      * @param {String} value The value to decode
42915      * @return {Mixed} The decoded value
42916      */
42917     decodeValue : function(cookie){
42918         var re = /^(a|n|d|b|s|o)\:(.*)$/;
42919         var matches = re.exec(unescape(cookie));
42920         if(!matches || !matches[1]) return; // non state cookie
42921         var type = matches[1];
42922         var v = matches[2];
42923         switch(type){
42924             case "n":
42925                 return parseFloat(v);
42926             case "d":
42927                 return new Date(Date.parse(v));
42928             case "b":
42929                 return (v == "1");
42930             case "a":
42931                 var all = [];
42932                 if(v != ''){
42933                     Ext.each(v.split('^'), function(val){
42934                         all.push(this.decodeValue(val));
42935                     }, this);
42936                 }
42937                 return all;
42938            case "o":
42939                 var all = {};
42940                 if(v != ''){
42941                     Ext.each(v.split('^'), function(val){
42942                         var kv = val.split('=');
42943                         all[kv[0]] = this.decodeValue(kv[1]);
42944                     }, this);
42945                 }
42946                 return all;
42947            default:
42948                 return v;
42949         }
42950     },
42951
42952     /**
42953      * Encodes a value including type information.  Decode with {@link #decodeValue}.
42954      * @param {Mixed} value The value to encode
42955      * @return {String} The encoded value
42956      */
42957     encodeValue : function(v){
42958         var enc;
42959         if(typeof v == "number"){
42960             enc = "n:" + v;
42961         }else if(typeof v == "boolean"){
42962             enc = "b:" + (v ? "1" : "0");
42963         }else if(Ext.isDate(v)){
42964             enc = "d:" + v.toGMTString();
42965         }else if(Ext.isArray(v)){
42966             var flat = "";
42967             for(var i = 0, len = v.length; i < len; i++){
42968                 flat += this.encodeValue(v[i]);
42969                 if(i != len-1) flat += "^";
42970             }
42971             enc = "a:" + flat;
42972         }else if(typeof v == "object"){
42973             var flat = "";
42974             for(var key in v){
42975                 if(typeof v[key] != "function" && v[key] !== undefined){
42976                     flat += key + "=" + this.encodeValue(v[key]) + "^";
42977                 }
42978             }
42979             enc = "o:" + flat.substring(0, flat.length-1);
42980         }else{
42981             enc = "s:" + v;
42982         }
42983         return escape(enc);
42984     }
42985 });
42986 /**
42987  * @class Ext.state.Manager
42988  * This is the global state manager. By default all components that are "state aware" check this class
42989  * for state information if you don't pass them a custom state provider. In order for this class
42990  * to be useful, it must be initialized with a provider when your application initializes. Example usage:
42991  <pre><code>
42992 // in your initialization function
42993 init : function(){
42994    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
42995    var win = new Window(...);
42996    win.restoreState();
42997 }
42998  </code></pre>
42999  * @singleton
43000  */
43001 Ext.state.Manager = function(){
43002     var provider = new Ext.state.Provider();
43003
43004     return {
43005         /**
43006          * Configures the default state provider for your application
43007          * @param {Provider} stateProvider The state provider to set
43008          */
43009         setProvider : function(stateProvider){
43010             provider = stateProvider;
43011         },
43012
43013         /**
43014          * Returns the current value for a key
43015          * @param {String} name The key name
43016          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
43017          * @return {Mixed} The state data
43018          */
43019         get : function(key, defaultValue){
43020             return provider.get(key, defaultValue);
43021         },
43022
43023         /**
43024          * Sets the value for a key
43025          * @param {String} name The key name
43026          * @param {Mixed} value The state data
43027          */
43028          set : function(key, value){
43029             provider.set(key, value);
43030         },
43031
43032         /**
43033          * Clears a value from the state
43034          * @param {String} name The key name
43035          */
43036         clear : function(key){
43037             provider.clear(key);
43038         },
43039
43040         /**
43041          * Gets the currently configured state provider
43042          * @return {Provider} The state provider
43043          */
43044         getProvider : function(){
43045             return provider;
43046         }
43047     };
43048 }();
43049 /**
43050  * @class Ext.state.CookieProvider
43051  * @extends Ext.state.Provider
43052  * The default Provider implementation which saves state via cookies.
43053  * <br />Usage:
43054  <pre><code>
43055    var cp = new Ext.state.CookieProvider({
43056        path: "/cgi-bin/",
43057        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days
43058        domain: "extjs.com"
43059    });
43060    Ext.state.Manager.setProvider(cp);
43061  </code></pre>
43062  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
43063  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
43064  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
43065  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include
43066  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
43067  * domain the page is running on including the 'www' like 'www.extjs.com')
43068  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
43069  * @constructor
43070  * Create a new CookieProvider
43071  * @param {Object} config The configuration object
43072  */
43073 Ext.state.CookieProvider = function(config){
43074     Ext.state.CookieProvider.superclass.constructor.call(this);
43075     this.path = "/";
43076     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
43077     this.domain = null;
43078     this.secure = false;
43079     Ext.apply(this, config);
43080     this.state = this.readCookies();
43081 };
43082
43083 Ext.extend(Ext.state.CookieProvider, Ext.state.Provider, {
43084     // private
43085     set : function(name, value){
43086         if(typeof value == "undefined" || value === null){
43087             this.clear(name);
43088             return;
43089         }
43090         this.setCookie(name, value);
43091         Ext.state.CookieProvider.superclass.set.call(this, name, value);
43092     },
43093
43094     // private
43095     clear : function(name){
43096         this.clearCookie(name);
43097         Ext.state.CookieProvider.superclass.clear.call(this, name);
43098     },
43099
43100     // private
43101     readCookies : function(){
43102         var cookies = {};
43103         var c = document.cookie + ";";
43104         var re = /\s?(.*?)=(.*?);/g;
43105         var matches;
43106         while((matches = re.exec(c)) != null){
43107             var name = matches[1];
43108             var value = matches[2];
43109             if(name && name.substring(0,3) == "ys-"){
43110                 cookies[name.substr(3)] = this.decodeValue(value);
43111             }
43112         }
43113         return cookies;
43114     },
43115
43116     // private
43117     setCookie : function(name, value){
43118         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
43119            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
43120            ((this.path == null) ? "" : ("; path=" + this.path)) +
43121            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
43122            ((this.secure == true) ? "; secure" : "");
43123     },
43124
43125     // private
43126     clearCookie : function(name){
43127         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
43128            ((this.path == null) ? "" : ("; path=" + this.path)) +
43129            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
43130            ((this.secure == true) ? "; secure" : "");
43131     }
43132 });/**
43133  * @class Ext.DataView
43134  * @extends Ext.BoxComponent
43135  * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
43136  * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
43137  * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
43138  * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
43139  * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
43140  * config must be provided for the DataView to determine what nodes it will be working with.</b>
43141  *
43142  * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.Panel}.</p>
43143  * <pre><code>
43144 var store = new Ext.data.JsonStore({
43145     url: 'get-images.php',
43146     root: 'images',
43147     fields: [
43148         'name', 'url',
43149         {name:'size', type: 'float'},
43150         {name:'lastmod', type:'date', dateFormat:'timestamp'}
43151     ]
43152 });
43153 store.load();
43154
43155 var tpl = new Ext.XTemplate(
43156     '&lt;tpl for="."&gt;',
43157         '&lt;div class="thumb-wrap" id="{name}"&gt;',
43158         '&lt;div class="thumb"&gt;&lt;img src="{url}" title="{name}"&gt;&lt;/div&gt;',
43159         '&lt;span class="x-editable"&gt;{shortName}&lt;/span&gt;&lt;/div&gt;',
43160     '&lt;/tpl&gt;',
43161     '&lt;div class="x-clear"&gt;&lt;/div&gt;'
43162 );
43163
43164 var panel = new Ext.Panel({
43165     id:'images-view',
43166     frame:true,
43167     width:535,
43168     autoHeight:true,
43169     collapsible:true,
43170     layout:'fit',
43171     title:'Simple DataView',
43172
43173     items: new Ext.DataView({
43174         store: store,
43175         tpl: tpl,
43176         autoHeight:true,
43177         multiSelect: true,
43178         overClass:'x-view-over',
43179         itemSelector:'div.thumb-wrap',
43180         emptyText: 'No images to display'
43181     })
43182 });
43183 panel.render(document.body);
43184 </code></pre>
43185  * @constructor
43186  * Create a new DataView
43187  * @param {Object} config The config object
43188  * @xtype dataview
43189  */
43190 Ext.DataView = Ext.extend(Ext.BoxComponent, {
43191     /**
43192      * @cfg {String/Array} tpl
43193      * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
43194      * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
43195      */
43196     /**
43197      * @cfg {Ext.data.Store} store
43198      * The {@link Ext.data.Store} to bind this DataView to.
43199      */
43200     /**
43201      * @cfg {String} itemSelector
43202      * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or 
43203      * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
43204      * working with.
43205      */
43206     /**
43207      * @cfg {Boolean} multiSelect
43208      * True to allow selection of more than one item at a time, false to allow selection of only a single item
43209      * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
43210      */
43211     /**
43212      * @cfg {Boolean} singleSelect
43213      * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
43214      * Note that if {@link #multiSelect} = true, this value will be ignored.
43215      */
43216     /**
43217      * @cfg {Boolean} simpleSelect
43218      * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
43219      * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
43220      */
43221     /**
43222      * @cfg {String} overClass
43223      * A CSS class to apply to each item in the view on mouseover (defaults to undefined).
43224      */
43225     /**
43226      * @cfg {String} loadingText
43227      * A string to display during data load operations (defaults to undefined).  If specified, this text will be
43228      * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
43229      * contents will continue to display normally until the new data is loaded and the contents are replaced.
43230      */
43231     /**
43232      * @cfg {String} selectedClass
43233      * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
43234      */
43235     selectedClass : "x-view-selected",
43236     /**
43237      * @cfg {String} emptyText
43238      * The text to display in the view when there is no data to display (defaults to '').
43239      */
43240     emptyText : "",
43241
43242     /**
43243      * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
43244      */
43245     deferEmptyText: true,
43246     /**
43247      * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
43248      */
43249     trackOver: false,
43250     
43251     /**
43252      * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
43253      * you wish to provide custom transition animations via a plugin (defaults to false)
43254      */
43255     blockRefresh: false,
43256
43257     //private
43258     last: false,
43259
43260     // private
43261     initComponent : function(){
43262         Ext.DataView.superclass.initComponent.call(this);
43263         if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
43264             this.tpl = new Ext.XTemplate(this.tpl);
43265         }
43266
43267         this.addEvents(
43268             /**
43269              * @event beforeclick
43270              * Fires before a click is processed. Returns false to cancel the default action.
43271              * @param {Ext.DataView} this
43272              * @param {Number} index The index of the target node
43273              * @param {HTMLElement} node The target node
43274              * @param {Ext.EventObject} e The raw event object
43275              */
43276             "beforeclick",
43277             /**
43278              * @event click
43279              * Fires when a template node is clicked.
43280              * @param {Ext.DataView} this
43281              * @param {Number} index The index of the target node
43282              * @param {HTMLElement} node The target node
43283              * @param {Ext.EventObject} e The raw event object
43284              */
43285             "click",
43286             /**
43287              * @event mouseenter
43288              * Fires when the mouse enters a template node. trackOver:true or an overClass must be set to enable this event.
43289              * @param {Ext.DataView} this
43290              * @param {Number} index The index of the target node
43291              * @param {HTMLElement} node The target node
43292              * @param {Ext.EventObject} e The raw event object
43293              */
43294             "mouseenter",
43295             /**
43296              * @event mouseleave
43297              * Fires when the mouse leaves a template node. trackOver:true or an overClass must be set to enable this event.
43298              * @param {Ext.DataView} this
43299              * @param {Number} index The index of the target node
43300              * @param {HTMLElement} node The target node
43301              * @param {Ext.EventObject} e The raw event object
43302              */
43303             "mouseleave",
43304             /**
43305              * @event containerclick
43306              * Fires when a click occurs and it is not on a template node.
43307              * @param {Ext.DataView} this
43308              * @param {Ext.EventObject} e The raw event object
43309              */
43310             "containerclick",
43311             /**
43312              * @event dblclick
43313              * Fires when a template node is double clicked.
43314              * @param {Ext.DataView} this
43315              * @param {Number} index The index of the target node
43316              * @param {HTMLElement} node The target node
43317              * @param {Ext.EventObject} e The raw event object
43318              */
43319             "dblclick",
43320             /**
43321              * @event contextmenu
43322              * Fires when a template node is right clicked.
43323              * @param {Ext.DataView} this
43324              * @param {Number} index The index of the target node
43325              * @param {HTMLElement} node The target node
43326              * @param {Ext.EventObject} e The raw event object
43327              */
43328             "contextmenu",
43329             /**
43330              * @event containercontextmenu
43331              * Fires when a right click occurs that is not on a template node.
43332              * @param {Ext.DataView} this
43333              * @param {Ext.EventObject} e The raw event object
43334              */
43335             "containercontextmenu",
43336             /**
43337              * @event selectionchange
43338              * Fires when the selected nodes change.
43339              * @param {Ext.DataView} this
43340              * @param {Array} selections Array of the selected nodes
43341              */
43342             "selectionchange",
43343
43344             /**
43345              * @event beforeselect
43346              * Fires before a selection is made. If any handlers return false, the selection is cancelled.
43347              * @param {Ext.DataView} this
43348              * @param {HTMLElement} node The node to be selected
43349              * @param {Array} selections Array of currently selected nodes
43350              */
43351             "beforeselect"
43352         );
43353
43354         this.store = Ext.StoreMgr.lookup(this.store);
43355         this.all = new Ext.CompositeElementLite();
43356         this.selected = new Ext.CompositeElementLite();
43357     },
43358
43359     // private
43360     afterRender : function(){
43361         Ext.DataView.superclass.afterRender.call(this);
43362
43363                 this.mon(this.getTemplateTarget(), {
43364             "click": this.onClick,
43365             "dblclick": this.onDblClick,
43366             "contextmenu": this.onContextMenu,
43367             scope:this
43368         });
43369
43370         if(this.overClass || this.trackOver){
43371             this.mon(this.getTemplateTarget(), {
43372                 "mouseover": this.onMouseOver,
43373                 "mouseout": this.onMouseOut,
43374                 scope:this
43375             });
43376         }
43377
43378         if(this.store){
43379             this.bindStore(this.store, true);
43380         }
43381     },
43382
43383     /**
43384      * Refreshes the view by reloading the data from the store and re-rendering the template.
43385      */
43386     refresh : function() {
43387         this.clearSelections(false, true);
43388         var el = this.getTemplateTarget();
43389         el.update("");
43390         var records = this.store.getRange();
43391         if(records.length < 1){
43392             if(!this.deferEmptyText || this.hasSkippedEmptyText){
43393                 el.update(this.emptyText);
43394             }
43395             this.all.clear();
43396         }else{
43397             this.tpl.overwrite(el, this.collectData(records, 0));
43398             this.all.fill(Ext.query(this.itemSelector, el.dom));
43399             this.updateIndexes(0);
43400         }
43401         this.hasSkippedEmptyText = true;
43402     },
43403
43404     getTemplateTarget: function(){
43405         return this.el;
43406     },
43407
43408     /**
43409      * Function which can be overridden to provide custom formatting for each Record that is used by this
43410      * DataView's {@link #tpl template} to render each node.
43411      * @param {Array/Object} data The raw data object that was used to create the Record.
43412      * @param {Number} recordIndex the index number of the Record being prepared for rendering.
43413      * @param {Record} record The Record being prepared for rendering.
43414      * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
43415      * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
43416      */
43417     prepareData : function(data){
43418         return data;
43419     },
43420
43421     /**
43422      * <p>Function which can be overridden which returns the data object passed to this
43423      * DataView's {@link #tpl template} to render the whole DataView.</p>
43424      * <p>This is usually an Array of data objects, each element of which is processed by an
43425      * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
43426      * data object as an Array. However, <i>named</i> properties may be placed into the data object to
43427      * provide non-repeating data such as headings, totals etc.</p>
43428      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
43429      * @param {Number} startIndex the index number of the Record being prepared for rendering.
43430      * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
43431      * contain <i>named</i> properties.
43432      */
43433     collectData : function(records, startIndex){
43434         var r = [];
43435         for(var i = 0, len = records.length; i < len; i++){
43436             r[r.length] = this.prepareData(records[i].data, startIndex+i, records[i]);
43437         }
43438         return r;
43439     },
43440
43441     // private
43442     bufferRender : function(records){
43443         var div = document.createElement('div');
43444         this.tpl.overwrite(div, this.collectData(records));
43445         return Ext.query(this.itemSelector, div);
43446     },
43447
43448     // private
43449     onUpdate : function(ds, record){
43450         var index = this.store.indexOf(record);
43451         if(index > -1){
43452             var sel = this.isSelected(index);
43453             var original = this.all.elements[index];
43454             var node = this.bufferRender([record], index)[0];
43455
43456             this.all.replaceElement(index, node, true);
43457             if(sel){
43458                 this.selected.replaceElement(original, node);
43459                 this.all.item(index).addClass(this.selectedClass);
43460             }
43461             this.updateIndexes(index, index);
43462         }
43463     },
43464
43465     // private
43466     onAdd : function(ds, records, index){
43467         if(this.all.getCount() === 0){
43468             this.refresh();
43469             return;
43470         }
43471         var nodes = this.bufferRender(records, index), n, a = this.all.elements;
43472         if(index < this.all.getCount()){
43473             n = this.all.item(index).insertSibling(nodes, 'before', true);
43474             a.splice.apply(a, [index, 0].concat(nodes));
43475         }else{
43476             n = this.all.last().insertSibling(nodes, 'after', true);
43477             a.push.apply(a, nodes);
43478         }
43479         this.updateIndexes(index);
43480     },
43481
43482     // private
43483     onRemove : function(ds, record, index){
43484         this.deselect(index);
43485         this.all.removeElement(index, true);
43486         this.updateIndexes(index);
43487         if (this.store.getCount() === 0){
43488             this.refresh();
43489         }
43490     },
43491
43492     /**
43493      * Refreshes an individual node's data from the store.
43494      * @param {Number} index The item's data index in the store
43495      */
43496     refreshNode : function(index){
43497         this.onUpdate(this.store, this.store.getAt(index));
43498     },
43499
43500     // private
43501     updateIndexes : function(startIndex, endIndex){
43502         var ns = this.all.elements;
43503         startIndex = startIndex || 0;
43504         endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
43505         for(var i = startIndex; i <= endIndex; i++){
43506             ns[i].viewIndex = i;
43507         }
43508     },
43509     
43510     /**
43511      * Returns the store associated with this DataView.
43512      * @return {Ext.data.Store} The store
43513      */
43514     getStore : function(){
43515         return this.store;
43516     },
43517
43518     /**
43519      * Changes the data store bound to this view and refreshes it.
43520      * @param {Store} store The store to bind to this view
43521      */
43522     bindStore : function(store, initial){
43523         if(!initial && this.store){
43524             if(store !== this.store && this.store.autoDestroy){
43525                 this.store.destroy();
43526             }else{
43527                 this.store.un("beforeload", this.onBeforeLoad, this);
43528                 this.store.un("datachanged", this.onDataChanged, this);
43529                 this.store.un("add", this.onAdd, this);
43530                 this.store.un("remove", this.onRemove, this);
43531                 this.store.un("update", this.onUpdate, this);
43532                 this.store.un("clear", this.refresh, this);
43533             }
43534             if(!store){
43535                 this.store = null;
43536             }
43537         }
43538         if(store){
43539             store = Ext.StoreMgr.lookup(store);
43540             store.on({
43541                 scope: this,
43542                 beforeload: this.onBeforeLoad,
43543                 datachanged: this.onDataChanged,
43544                 add: this.onAdd,
43545                 remove: this.onRemove,
43546                 update: this.onUpdate,
43547                 clear: this.refresh
43548             });
43549         }
43550         this.store = store;
43551         if(store){
43552             this.refresh();
43553         }
43554     },
43555     
43556     /**
43557      * @private
43558      * Calls this.refresh if this.blockRefresh is not true
43559      */
43560     onDataChanged: function() {
43561         if (this.blockRefresh !== true) {
43562             this.refresh.apply(this, arguments);
43563         }
43564     },
43565
43566     /**
43567      * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
43568      * @param {HTMLElement} node
43569      * @return {HTMLElement} The template node
43570      */
43571     findItemFromChild : function(node){
43572         return Ext.fly(node).findParent(this.itemSelector, this.getTemplateTarget());
43573     },
43574
43575     // private
43576     onClick : function(e){
43577         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43578         if(item){
43579             var index = this.indexOf(item);
43580             if(this.onItemClick(item, index, e) !== false){
43581                 this.fireEvent("click", this, index, item, e);
43582             }
43583         }else{
43584             if(this.fireEvent("containerclick", this, e) !== false){
43585                 this.onContainerClick(e);
43586             }
43587         }
43588     },
43589
43590     onContainerClick : function(e){
43591         this.clearSelections();
43592     },
43593
43594     // private
43595     onContextMenu : function(e){
43596         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43597         if(item){
43598             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
43599         }else{
43600             this.fireEvent("containercontextmenu", this, e);
43601         }
43602     },
43603
43604     // private
43605     onDblClick : function(e){
43606         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43607         if(item){
43608             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
43609         }
43610     },
43611
43612     // private
43613     onMouseOver : function(e){
43614         var item = e.getTarget(this.itemSelector, this.getTemplateTarget());
43615         if(item && item !== this.lastItem){
43616             this.lastItem = item;
43617             Ext.fly(item).addClass(this.overClass);
43618             this.fireEvent("mouseenter", this, this.indexOf(item), item, e);
43619         }
43620     },
43621
43622     // private
43623     onMouseOut : function(e){
43624         if(this.lastItem){
43625             if(!e.within(this.lastItem, true, true)){
43626                 Ext.fly(this.lastItem).removeClass(this.overClass);
43627                 this.fireEvent("mouseleave", this, this.indexOf(this.lastItem), this.lastItem, e);
43628                 delete this.lastItem;
43629             }
43630         }
43631     },
43632
43633     // private
43634     onItemClick : function(item, index, e){
43635         if(this.fireEvent("beforeclick", this, index, item, e) === false){
43636             return false;
43637         }
43638         if(this.multiSelect){
43639             this.doMultiSelection(item, index, e);
43640             e.preventDefault();
43641         }else if(this.singleSelect){
43642             this.doSingleSelection(item, index, e);
43643             e.preventDefault();
43644         }
43645         return true;
43646     },
43647
43648     // private
43649     doSingleSelection : function(item, index, e){
43650         if(e.ctrlKey && this.isSelected(index)){
43651             this.deselect(index);
43652         }else{
43653             this.select(index, false);
43654         }
43655     },
43656
43657     // private
43658     doMultiSelection : function(item, index, e){
43659         if(e.shiftKey && this.last !== false){
43660             var last = this.last;
43661             this.selectRange(last, index, e.ctrlKey);
43662             this.last = last; // reset the last
43663         }else{
43664             if((e.ctrlKey||this.simpleSelect) && this.isSelected(index)){
43665                 this.deselect(index);
43666             }else{
43667                 this.select(index, e.ctrlKey || e.shiftKey || this.simpleSelect);
43668             }
43669         }
43670     },
43671
43672     /**
43673      * Gets the number of selected nodes.
43674      * @return {Number} The node count
43675      */
43676     getSelectionCount : function(){
43677         return this.selected.getCount();
43678     },
43679
43680     /**
43681      * Gets the currently selected nodes.
43682      * @return {Array} An array of HTMLElements
43683      */
43684     getSelectedNodes : function(){
43685         return this.selected.elements;
43686     },
43687
43688     /**
43689      * Gets the indexes of the selected nodes.
43690      * @return {Array} An array of numeric indexes
43691      */
43692     getSelectedIndexes : function(){
43693         var indexes = [], s = this.selected.elements;
43694         for(var i = 0, len = s.length; i < len; i++){
43695             indexes.push(s[i].viewIndex);
43696         }
43697         return indexes;
43698     },
43699
43700     /**
43701      * Gets an array of the selected records
43702      * @return {Array} An array of {@link Ext.data.Record} objects
43703      */
43704     getSelectedRecords : function(){
43705         var r = [], s = this.selected.elements;
43706         for(var i = 0, len = s.length; i < len; i++){
43707             r[r.length] = this.store.getAt(s[i].viewIndex);
43708         }
43709         return r;
43710     },
43711
43712     /**
43713      * Gets an array of the records from an array of nodes
43714      * @param {Array} nodes The nodes to evaluate
43715      * @return {Array} records The {@link Ext.data.Record} objects
43716      */
43717     getRecords : function(nodes){
43718         var r = [], s = nodes;
43719         for(var i = 0, len = s.length; i < len; i++){
43720             r[r.length] = this.store.getAt(s[i].viewIndex);
43721         }
43722         return r;
43723     },
43724
43725     /**
43726      * Gets a record from a node
43727      * @param {HTMLElement} node The node to evaluate
43728      * @return {Record} record The {@link Ext.data.Record} object
43729      */
43730     getRecord : function(node){
43731         return this.store.getAt(node.viewIndex);
43732     },
43733
43734     /**
43735      * Clears all selections.
43736      * @param {Boolean} suppressEvent (optional) True to skip firing of the selectionchange event
43737      */
43738     clearSelections : function(suppressEvent, skipUpdate){
43739         if((this.multiSelect || this.singleSelect) && this.selected.getCount() > 0){
43740             if(!skipUpdate){
43741                 this.selected.removeClass(this.selectedClass);
43742             }
43743             this.selected.clear();
43744             this.last = false;
43745             if(!suppressEvent){
43746                 this.fireEvent("selectionchange", this, this.selected.elements);
43747             }
43748         }
43749     },
43750
43751     /**
43752      * Returns true if the passed node is selected, else false.
43753      * @param {HTMLElement/Number/Ext.data.Record} node The node, node index or record to check
43754      * @return {Boolean} True if selected, else false
43755      */
43756     isSelected : function(node){
43757         return this.selected.contains(this.getNode(node));
43758     },
43759
43760     /**
43761      * Deselects a node.
43762      * @param {HTMLElement/Number/Record} node The node, node index or record to deselect
43763      */
43764     deselect : function(node){
43765         if(this.isSelected(node)){
43766             node = this.getNode(node);
43767             this.selected.removeElement(node);
43768             if(this.last == node.viewIndex){
43769                 this.last = false;
43770             }
43771             Ext.fly(node).removeClass(this.selectedClass);
43772             this.fireEvent("selectionchange", this, this.selected.elements);
43773         }
43774     },
43775
43776     /**
43777      * Selects a set of nodes.
43778      * @param {Array/HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node,
43779      * id of a template node, record associated with a node or an array of any of those to select
43780      * @param {Boolean} keepExisting (optional) true to keep existing selections
43781      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
43782      */
43783     select : function(nodeInfo, keepExisting, suppressEvent){
43784         if(Ext.isArray(nodeInfo)){
43785             if(!keepExisting){
43786                 this.clearSelections(true);
43787             }
43788             for(var i = 0, len = nodeInfo.length; i < len; i++){
43789                 this.select(nodeInfo[i], true, true);
43790             }
43791             if(!suppressEvent){
43792                 this.fireEvent("selectionchange", this, this.selected.elements);
43793             }
43794         } else{
43795             var node = this.getNode(nodeInfo);
43796             if(!keepExisting){
43797                 this.clearSelections(true);
43798             }
43799             if(node && !this.isSelected(node)){
43800                 if(this.fireEvent("beforeselect", this, node, this.selected.elements) !== false){
43801                     Ext.fly(node).addClass(this.selectedClass);
43802                     this.selected.add(node);
43803                     this.last = node.viewIndex;
43804                     if(!suppressEvent){
43805                         this.fireEvent("selectionchange", this, this.selected.elements);
43806                     }
43807                 }
43808             }
43809         }
43810     },
43811
43812     /**
43813      * Selects a range of nodes. All nodes between start and end are selected.
43814      * @param {Number} start The index of the first node in the range
43815      * @param {Number} end The index of the last node in the range
43816      * @param {Boolean} keepExisting (optional) True to retain existing selections
43817      */
43818     selectRange : function(start, end, keepExisting){
43819         if(!keepExisting){
43820             this.clearSelections(true);
43821         }
43822         this.select(this.getNodes(start, end), true);
43823     },
43824
43825     /**
43826      * Gets a template node.
43827      * @param {HTMLElement/String/Number/Ext.data.Record} nodeInfo An HTMLElement template node, index of a template node, 
43828      * the id of a template node or the record associated with the node.
43829      * @return {HTMLElement} The node or null if it wasn't found
43830      */
43831     getNode : function(nodeInfo){
43832         if(Ext.isString(nodeInfo)){
43833             return document.getElementById(nodeInfo);
43834         }else if(Ext.isNumber(nodeInfo)){
43835             return this.all.elements[nodeInfo];
43836         }else if(nodeInfo instanceof Ext.data.Record){
43837             var idx = this.store.indexOf(nodeInfo);
43838             return this.all.elements[idx];
43839         }
43840         return nodeInfo;
43841     },
43842
43843     /**
43844      * Gets a range nodes.
43845      * @param {Number} start (optional) The index of the first node in the range
43846      * @param {Number} end (optional) The index of the last node in the range
43847      * @return {Array} An array of nodes
43848      */
43849     getNodes : function(start, end){
43850         var ns = this.all.elements;
43851         start = start || 0;
43852         end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
43853         var nodes = [], i;
43854         if(start <= end){
43855             for(i = start; i <= end && ns[i]; i++){
43856                 nodes.push(ns[i]);
43857             }
43858         } else{
43859             for(i = start; i >= end && ns[i]; i--){
43860                 nodes.push(ns[i]);
43861             }
43862         }
43863         return nodes;
43864     },
43865
43866     /**
43867      * Finds the index of the passed node.
43868      * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
43869      * or a record associated with a node.
43870      * @return {Number} The index of the node or -1
43871      */
43872     indexOf : function(node){
43873         node = this.getNode(node);
43874         if(Ext.isNumber(node.viewIndex)){
43875             return node.viewIndex;
43876         }
43877         return this.all.indexOf(node);
43878     },
43879
43880     // private
43881     onBeforeLoad : function(){
43882         if(this.loadingText){
43883             this.clearSelections(false, true);
43884             this.getTemplateTarget().update('<div class="loading-indicator">'+this.loadingText+'</div>');
43885             this.all.clear();
43886         }
43887     },
43888
43889     onDestroy : function(){
43890         this.all.clear();
43891         this.selected.clear();
43892         Ext.DataView.superclass.onDestroy.call(this);
43893         this.bindStore(null);
43894     }
43895 });
43896
43897 /**
43898  * Changes the data store bound to this view and refreshes it. (deprecated in favor of bindStore)
43899  * @param {Store} store The store to bind to this view
43900  */
43901 Ext.DataView.prototype.setStore = Ext.DataView.prototype.bindStore;
43902
43903 Ext.reg('dataview', Ext.DataView);
43904 /**
43905  * @class Ext.list.ListView
43906  * @extends Ext.DataView
43907  * <p>Ext.list.ListView is a fast and light-weight implentation of a
43908  * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p>
43909  * <div class="mdetail-params"><ul>
43910  * <li>resizable columns</li>
43911  * <li>selectable</li>
43912  * <li>column widths are initially proportioned by percentage based on the container
43913  * width and number of columns</li>
43914  * <li>uses templates to render the data in any required format</li>
43915  * <li>no horizontal scrolling</li>
43916  * <li>no editing</li>
43917  * </ul></div>
43918  * <p>Example usage:</p>
43919  * <pre><code>
43920 // consume JSON of this form:
43921 {
43922    "images":[
43923       {
43924          "name":"dance_fever.jpg",
43925          "size":2067,
43926          "lastmod":1236974993000,
43927          "url":"images\/thumbs\/dance_fever.jpg"
43928       },
43929       {
43930          "name":"zack_sink.jpg",
43931          "size":2303,
43932          "lastmod":1236974993000,
43933          "url":"images\/thumbs\/zack_sink.jpg"
43934       }
43935    ]
43936 }
43937 var store = new Ext.data.JsonStore({
43938     url: 'get-images.php',
43939     root: 'images',
43940     fields: [
43941         'name', 'url',
43942         {name:'size', type: 'float'},
43943         {name:'lastmod', type:'date', dateFormat:'timestamp'}
43944     ]
43945 });
43946 store.load();
43947
43948 var listView = new Ext.list.ListView({
43949     store: store,
43950     multiSelect: true,
43951     emptyText: 'No images to display',
43952     reserveScrollOffset: true,
43953     columns: [{
43954         header: 'File',
43955         width: .5,
43956         dataIndex: 'name'
43957     },{
43958         header: 'Last Modified',
43959         width: .35,
43960         dataIndex: 'lastmod',
43961         tpl: '{lastmod:date("m-d h:i a")}'
43962     },{
43963         header: 'Size',
43964         dataIndex: 'size',
43965         tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize()
43966         align: 'right'
43967     }]
43968 });
43969
43970 // put it in a Panel so it looks pretty
43971 var panel = new Ext.Panel({
43972     id:'images-view',
43973     width:425,
43974     height:250,
43975     collapsible:true,
43976     layout:'fit',
43977     title:'Simple ListView <i>(0 items selected)</i>',
43978     items: listView
43979 });
43980 panel.render(document.body);
43981
43982 // little bit of feedback
43983 listView.on('selectionchange', function(view, nodes){
43984     var l = nodes.length;
43985     var s = l != 1 ? 's' : '';
43986     panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>');
43987 });
43988  * </code></pre>
43989  * @constructor
43990  * @param {Object} config
43991  * @xtype listview
43992  */
43993 Ext.list.ListView = Ext.extend(Ext.DataView, {
43994     /**
43995      * Set this property to <tt>true</tt> to disable the header click handler disabling sort
43996      * (defaults to <tt>false</tt>).
43997      * @type Boolean
43998      * @property disableHeaders
43999      */
44000     /**
44001      * @cfg {Boolean} hideHeaders
44002      * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so
44003      * the {@link #internalTpl header row} will be shown).
44004      */
44005     /**
44006      * @cfg {String} itemSelector
44007      * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>.
44008      * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>)
44009      * that will be used to determine what nodes the ListView will be working with.
44010      */
44011     itemSelector: 'dl',
44012     /**
44013      * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to
44014      * <tt>'x-list-selected'</tt>). An example overriding the default styling:
44015     <pre><code>
44016     .x-list-selected {background-color: yellow;}
44017     </code></pre>
44018      * @type String
44019      */
44020     selectedClass:'x-list-selected',
44021     /**
44022      * @cfg {String} overClass The CSS class applied when over a row (defaults to
44023      * <tt>'x-list-over'</tt>). An example overriding the default styling:
44024     <pre><code>
44025     .x-list-over {background-color: orange;}
44026     </code></pre>
44027      * @type String
44028      */
44029     overClass:'x-list-over',
44030     /**
44031      * @cfg {Boolean} reserveScrollOffset
44032      * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b>
44033      * for 10 milliseconds.  Specify <tt>true</tt> to account for the configured
44034      * <b><tt>{@link #scrollOffset}</tt></b> immediately.
44035      */
44036     /**
44037      * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to
44038      * <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
44039      * calculated.
44040      */
44041     scrollOffset : undefined,
44042     /**
44043      * @cfg {Boolean/Object} columnResize
44044      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer}
44045      * to enable the columns to be resizable (defaults to <tt>true</tt>).
44046      */
44047     columnResize: true,
44048     /**
44049      * @cfg {Array} columns An array of column configuration objects, for example:
44050      * <pre><code>
44051 {
44052     align: 'right',
44053     dataIndex: 'size',
44054     header: 'Size',
44055     tpl: '{size:fileSize}',
44056     width: .35
44057 }
44058      * </code></pre>
44059      * Acceptable properties for each column configuration object are:
44060      * <div class="mdetail-params"><ul>
44061      * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property
44062      * of the column. Defaults to <tt>'left'</tt>.</div></li>
44063      * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
44064      * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li>
44065      * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}.
44066      * {@link Ext.grid.Column#header header} for details.</div></li>
44067      * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the
44068      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
44069      * will be implicitly created using the <tt>dataIndex</tt>.</div></li>
44070      * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width
44071      * this column should be allocated.  Columns that have no width specified will be
44072      * allocated with an equal percentage to fill 100% of the container width.  To easily take
44073      * advantage of the full container width, leave the width of at least one column undefined.
44074      * Note that if you do not want to take up the full width of the container, the width of
44075      * every column needs to be explicitly defined.</div></li>
44076      * </ul></div>
44077      */
44078     /**
44079      * @cfg {Boolean/Object} columnSort
44080      * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter}
44081      * to enable the columns to be sortable (defaults to <tt>true</tt>).
44082      */
44083     columnSort: true,
44084     /**
44085      * @cfg {String/Array} internalTpl
44086      * The template to be used for the header row.  See {@link #tpl} for more details.
44087      */
44088
44089     /*
44090      * IE has issues when setting percentage based widths to 100%. Default to 99.
44091      */
44092     maxWidth: Ext.isIE ? 99 : 100,
44093
44094     initComponent : function(){
44095         if(this.columnResize){
44096             this.colResizer = new Ext.list.ColumnResizer(this.colResizer);
44097             this.colResizer.init(this);
44098         }
44099         if(this.columnSort){
44100             this.colSorter = new Ext.list.Sorter(this.columnSort);
44101             this.colSorter.init(this);
44102         }
44103         if(!this.internalTpl){
44104             this.internalTpl = new Ext.XTemplate(
44105                 '<div class="x-list-header"><div class="x-list-header-inner">',
44106                     '<tpl for="columns">',
44107                     '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">',
44108                         '{header}',
44109                     '</em></div>',
44110                     '</tpl>',
44111                     '<div class="x-clear"></div>',
44112                 '</div></div>',
44113                 '<div class="x-list-body"><div class="x-list-body-inner">',
44114                 '</div></div>'
44115             );
44116         }
44117         if(!this.tpl){
44118             this.tpl = new Ext.XTemplate(
44119                 '<tpl for="rows">',
44120                     '<dl>',
44121                         '<tpl for="parent.columns">',
44122                         '<dt style="width:{[values.width*100]}%;text-align:{align};">',
44123                         '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">',
44124                             '{[values.tpl.apply(parent)]}',
44125                         '</em></dt>',
44126                         '</tpl>',
44127                         '<div class="x-clear"></div>',
44128                     '</dl>',
44129                 '</tpl>'
44130             );
44131         };
44132
44133         var cs = this.columns,
44134             allocatedWidth = 0,
44135             colsWithWidth = 0,
44136             len = cs.length,
44137             columns = [];
44138
44139         for(var i = 0; i < len; i++){
44140             var c = cs[i];
44141             if(!c.isColumn) {
44142                 c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn';
44143                 c = Ext.create(c);
44144             }
44145             if(c.width) {
44146                 allocatedWidth += c.width*100;
44147                 colsWithWidth++;
44148             }
44149             columns.push(c);
44150         }
44151
44152         cs = this.columns = columns;
44153
44154         // auto calculate missing column widths
44155         if(colsWithWidth < len){
44156             var remaining = len - colsWithWidth;
44157             if(allocatedWidth < this.maxWidth){
44158                 var perCol = ((this.maxWidth-allocatedWidth) / remaining)/100;
44159                 for(var j = 0; j < len; j++){
44160                     var c = cs[j];
44161                     if(!c.width){
44162                         c.width = perCol;
44163                     }
44164                 }
44165             }
44166         }
44167         Ext.list.ListView.superclass.initComponent.call(this);
44168     },
44169
44170     onRender : function(){
44171         this.autoEl = {
44172             cls: 'x-list-wrap'
44173         };
44174         Ext.list.ListView.superclass.onRender.apply(this, arguments);
44175
44176         this.internalTpl.overwrite(this.el, {columns: this.columns});
44177
44178         this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild);
44179         this.innerHd = Ext.get(this.el.dom.firstChild.firstChild);
44180
44181         if(this.hideHeaders){
44182             this.el.dom.firstChild.style.display = 'none';
44183         }
44184     },
44185
44186     getTemplateTarget : function(){
44187         return this.innerBody;
44188     },
44189
44190     /**
44191      * <p>Function which can be overridden which returns the data object passed to this
44192      * view's {@link #tpl template} to render the whole ListView. The returned object
44193      * shall contain the following properties:</p>
44194      * <div class="mdetail-params"><ul>
44195      * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li>
44196      * <li><b>rows</b> : String<div class="sub-desc">See
44197      * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li>
44198      * </ul></div>
44199      * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView.
44200      * @param {Number} startIndex the index number of the Record being prepared for rendering.
44201      * @return {Object} A data object containing properties to be processed by a repeating
44202      * XTemplate as described above.
44203      */
44204     collectData : function(){
44205         var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments);
44206         return {
44207             columns: this.columns,
44208             rows: rs
44209         }
44210     },
44211
44212     verifyInternalSize : function(){
44213         if(this.lastSize){
44214             this.onResize(this.lastSize.width, this.lastSize.height);
44215         }
44216     },
44217
44218     // private
44219     onResize : function(w, h){
44220         var bd = this.innerBody.dom;
44221         var hd = this.innerHd.dom;
44222         if(!bd){
44223             return;
44224         }
44225         var bdp = bd.parentNode;
44226         if(Ext.isNumber(w)){
44227             var sw = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
44228             if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){
44229                 bd.style.width = sw + 'px';
44230                 hd.style.width = sw + 'px';
44231             }else{
44232                 bd.style.width = w + 'px';
44233                 hd.style.width = w + 'px';
44234                 setTimeout(function(){
44235                     if((bdp.offsetWidth - bdp.clientWidth) > 10){
44236                         bd.style.width = sw + 'px';
44237                         hd.style.width = sw + 'px';
44238                     }
44239                 }, 10);
44240             }
44241         }
44242         if(Ext.isNumber(h)){
44243             bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';
44244         }
44245     },
44246
44247     updateIndexes : function(){
44248         Ext.list.ListView.superclass.updateIndexes.apply(this, arguments);
44249         this.verifyInternalSize();
44250     },
44251
44252     findHeaderIndex : function(hd){
44253         hd = hd.dom || hd;
44254         var pn = hd.parentNode, cs = pn.parentNode.childNodes;
44255         for(var i = 0, c; c = cs[i]; i++){
44256             if(c == pn){
44257                 return i;
44258             }
44259         }
44260         return -1;
44261     },
44262
44263     setHdWidths : function(){
44264         var els = this.innerHd.dom.getElementsByTagName('div');
44265         for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){
44266             els[i].style.width = (cs[i].width*100) + '%';
44267         }
44268     }
44269 });
44270
44271 Ext.reg('listview', Ext.list.ListView);
44272
44273 // Backwards compatibility alias
44274 Ext.ListView = Ext.list.ListView;/**
44275  * @class Ext.list.Column
44276  * <p>This class encapsulates column configuration data to be used in the initialization of a
44277  * {@link Ext.list.ListView ListView}.</p>
44278  * <p>While subclasses are provided to render data in different ways, this class renders a passed
44279  * data field unchanged and is usually used for textual columns.</p>
44280  */
44281 Ext.list.Column = Ext.extend(Object, {
44282     /**
44283      * @private
44284      * @cfg {Boolean} isColumn
44285      * Used by ListView constructor method to avoid reprocessing a Column
44286      * if <code>isColumn</code> is not set ListView will recreate a new Ext.list.Column
44287      * Defaults to true.
44288      */
44289     isColumn: true,
44290     
44291     /**
44292      * @cfg {String} align
44293      * Set the CSS text-align property of the column. Defaults to <tt>'left'</tt>.
44294      */        
44295     align: 'left',
44296     /**
44297      * @cfg {String} header Optional. The header text to be used as innerHTML
44298      * (html tags are accepted) to display in the ListView.  <b>Note</b>: to
44299      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
44300      */    
44301     header: '',
44302     
44303     /**
44304      * @cfg {Number} width Optional. Percentage of the container width
44305      * this column should be allocated.  Columns that have no width specified will be
44306      * allocated with an equal percentage to fill 100% of the container width.  To easily take
44307      * advantage of the full container width, leave the width of at least one column undefined.
44308      * Note that if you do not want to take up the full width of the container, the width of
44309      * every column needs to be explicitly defined.
44310      */    
44311     width: null,
44312
44313     /**
44314      * @cfg {String} cls Optional. This option can be used to add a CSS class to the cell of each
44315      * row for this column.
44316      */
44317     cls: '',
44318     
44319     /**
44320      * @cfg {String} tpl Optional. Specify a string to pass as the
44321      * configuration string for {@link Ext.XTemplate}.  By default an {@link Ext.XTemplate}
44322      * will be implicitly created using the <tt>dataIndex</tt>.
44323      */
44324
44325     /**
44326      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
44327      * ListViews's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
44328      * which to draw the column's value.</p>
44329      */
44330     
44331     constructor : function(c){
44332         if(!c.tpl){
44333             c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}');
44334         }
44335         else if(Ext.isString(c.tpl)){
44336             c.tpl = new Ext.XTemplate(c.tpl);
44337         }
44338         
44339         Ext.apply(this, c);
44340     }
44341 });
44342
44343 Ext.reg('lvcolumn', Ext.list.Column);
44344
44345 /**
44346  * @class Ext.list.NumberColumn
44347  * @extends Ext.list.Column
44348  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
44349  * {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column} for more details.</p>
44350  */
44351 Ext.list.NumberColumn = Ext.extend(Ext.list.Column, {
44352     /**
44353      * @cfg {String} format
44354      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
44355      * (defaults to <tt>'0,000.00'</tt>).
44356      */    
44357     format: '0,000.00',
44358     
44359     constructor : function(c) {
44360         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':number("' + (c.format || this.format) + '")}');       
44361         Ext.list.NumberColumn.superclass.constructor.call(this, c);
44362     }
44363 });
44364
44365 Ext.reg('lvnumbercolumn', Ext.list.NumberColumn);
44366
44367 /**
44368  * @class Ext.list.DateColumn
44369  * @extends Ext.list.Column
44370  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
44371  * {@link #format}. See the {@link Ext.list.Column#xtype xtype} config option of {@link Ext.list.Column}
44372  * for more details.</p>
44373  */
44374 Ext.list.DateColumn = Ext.extend(Ext.list.Column, {
44375     format: 'm/d/Y',
44376     constructor : function(c) {
44377         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':date("' + (c.format || this.format) + '")}');      
44378         Ext.list.DateColumn.superclass.constructor.call(this, c);
44379     }
44380 });
44381 Ext.reg('lvdatecolumn', Ext.list.DateColumn);
44382
44383 /**
44384  * @class Ext.list.BooleanColumn
44385  * @extends Ext.list.Column
44386  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.list.Column#xtype xtype}
44387  * config option of {@link Ext.list.Column} for more details.</p>
44388  */
44389 Ext.list.BooleanColumn = Ext.extend(Ext.list.Column, {
44390     /**
44391      * @cfg {String} trueText
44392      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
44393      */
44394     trueText: 'true',
44395     /**
44396      * @cfg {String} falseText
44397      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
44398      * <tt>'false'</tt>).
44399      */
44400     falseText: 'false',
44401     /**
44402      * @cfg {String} undefinedText
44403      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
44404      */
44405     undefinedText: '&#160;',
44406     
44407     constructor : function(c) {
44408         c.tpl = c.tpl || new Ext.XTemplate('{' + c.dataIndex + ':this.format}');
44409         
44410         var t = this.trueText, f = this.falseText, u = this.undefinedText;
44411         c.tpl.format = function(v){
44412             if(v === undefined){
44413                 return u;
44414             }
44415             if(!v || v === 'false'){
44416                 return f;
44417             }
44418             return t;
44419         };
44420         
44421         Ext.list.DateColumn.superclass.constructor.call(this, c);
44422     }
44423 });
44424
44425 Ext.reg('lvbooleancolumn', Ext.list.BooleanColumn);/**
44426  * @class Ext.list.ColumnResizer
44427  * @extends Ext.util.Observable
44428  * <p>Supporting Class for Ext.list.ListView</p>
44429  * @constructor
44430  * @param {Object} config
44431  */
44432 Ext.list.ColumnResizer = Ext.extend(Ext.util.Observable, {
44433     /**
44434      * @cfg {Number} minPct The minimum percentage to allot for any column (defaults to <tt>.05</tt>)
44435      */
44436     minPct: .05,
44437
44438     constructor: function(config){
44439         Ext.apply(this, config);
44440         Ext.list.ColumnResizer.superclass.constructor.call(this);
44441     },
44442     init : function(listView){
44443         this.view = listView;
44444         listView.on('render', this.initEvents, this);
44445     },
44446
44447     initEvents : function(view){
44448         view.mon(view.innerHd, 'mousemove', this.handleHdMove, this);
44449         this.tracker = new Ext.dd.DragTracker({
44450             onBeforeStart: this.onBeforeStart.createDelegate(this),
44451             onStart: this.onStart.createDelegate(this),
44452             onDrag: this.onDrag.createDelegate(this),
44453             onEnd: this.onEnd.createDelegate(this),
44454             tolerance: 3,
44455             autoStart: 300
44456         });
44457         this.tracker.initEl(view.innerHd);
44458         view.on('beforedestroy', this.tracker.destroy, this.tracker);
44459     },
44460
44461     handleHdMove : function(e, t){
44462         var hw = 5,
44463             x = e.getPageX(),
44464             hd = e.getTarget('em', 3, true);
44465         if(hd){
44466             var r = hd.getRegion(),
44467                 ss = hd.dom.style,
44468                 pn = hd.dom.parentNode;
44469
44470             if(x - r.left <= hw && pn != pn.parentNode.firstChild){
44471                 this.activeHd = Ext.get(pn.previousSibling.firstChild);
44472                 ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize';
44473             } else if(r.right - x <= hw && pn != pn.parentNode.lastChild.previousSibling){
44474                 this.activeHd = hd;
44475                 ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize';
44476             } else{
44477                 delete this.activeHd;
44478                 ss.cursor = '';
44479             }
44480         }
44481     },
44482
44483     onBeforeStart : function(e){
44484         this.dragHd = this.activeHd;
44485         return !!this.dragHd;
44486     },
44487
44488     onStart: function(e){
44489         this.view.disableHeaders = true;
44490         this.proxy = this.view.el.createChild({cls:'x-list-resizer'});
44491         this.proxy.setHeight(this.view.el.getHeight());
44492
44493         var x = this.tracker.getXY()[0],
44494             w = this.view.innerHd.getWidth();
44495
44496         this.hdX = this.dragHd.getX();
44497         this.hdIndex = this.view.findHeaderIndex(this.dragHd);
44498
44499         this.proxy.setX(this.hdX);
44500         this.proxy.setWidth(x-this.hdX);
44501
44502         this.minWidth = w*this.minPct;
44503         this.maxWidth = w - (this.minWidth*(this.view.columns.length-1-this.hdIndex));
44504     },
44505
44506     onDrag: function(e){
44507         var cursorX = this.tracker.getXY()[0];
44508         this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth));
44509     },
44510
44511     onEnd: function(e){
44512         /* calculate desired width by measuring proxy and then remove it */
44513         var nw = this.proxy.getWidth();
44514         this.proxy.remove();
44515
44516         var index = this.hdIndex,
44517             vw = this.view,
44518             cs = vw.columns,
44519             len = cs.length,
44520             w = this.view.innerHd.getWidth(),
44521             minPct = this.minPct * 100,
44522             pct = Math.ceil((nw * vw.maxWidth) / w),
44523             diff = (cs[index].width * 100) - pct,
44524             eachItem = Math.floor(diff / (len-1-index)),
44525             mod = diff - (eachItem * (len-1-index));
44526
44527         for(var i = index+1; i < len; i++){
44528             var cw = (cs[i].width * 100) + eachItem,
44529                 ncw = Math.max(minPct, cw);
44530             if(cw != ncw){
44531                 mod += cw - ncw;
44532             }
44533             cs[i].width = ncw / 100;
44534         }
44535         cs[index].width = pct / 100;
44536         cs[index+1].width += (mod / 100);
44537         delete this.dragHd;
44538         vw.setHdWidths();
44539         vw.refresh();
44540         setTimeout(function(){
44541             vw.disableHeaders = false;
44542         }, 100);
44543     }
44544 });
44545
44546 // Backwards compatibility alias
44547 Ext.ListView.ColumnResizer = Ext.list.ColumnResizer;/**
44548  * @class Ext.list.Sorter
44549  * @extends Ext.util.Observable
44550  * <p>Supporting Class for Ext.list.ListView</p>
44551  * @constructor
44552  * @param {Object} config
44553  */
44554 Ext.list.Sorter = Ext.extend(Ext.util.Observable, {
44555     /**
44556      * @cfg {Array} sortClasses
44557      * The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)
44558      */
44559     sortClasses : ["sort-asc", "sort-desc"],
44560
44561     constructor: function(config){
44562         Ext.apply(this, config);
44563         Ext.list.Sorter.superclass.constructor.call(this);
44564     },
44565
44566     init : function(listView){
44567         this.view = listView;
44568         listView.on('render', this.initEvents, this);
44569     },
44570
44571     initEvents : function(view){
44572         view.mon(view.innerHd, 'click', this.onHdClick, this);
44573         view.innerHd.setStyle('cursor', 'pointer');
44574         view.mon(view.store, 'datachanged', this.updateSortState, this);
44575         this.updateSortState.defer(10, this, [view.store]);
44576     },
44577
44578     updateSortState : function(store){
44579         var state = store.getSortState();
44580         if(!state){
44581             return;
44582         }
44583         this.sortState = state;
44584         var cs = this.view.columns, sortColumn = -1;
44585         for(var i = 0, len = cs.length; i < len; i++){
44586             if(cs[i].dataIndex == state.field){
44587                 sortColumn = i;
44588                 break;
44589             }
44590         }
44591         if(sortColumn != -1){
44592             var sortDir = state.direction;
44593             this.updateSortIcon(sortColumn, sortDir);
44594         }
44595     },
44596
44597     updateSortIcon : function(col, dir){
44598         var sc = this.sortClasses;
44599         var hds = this.view.innerHd.select('em').removeClass(sc);
44600         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
44601     },
44602
44603     onHdClick : function(e){
44604         var hd = e.getTarget('em', 3);
44605         if(hd && !this.view.disableHeaders){
44606             var index = this.view.findHeaderIndex(hd);
44607             this.view.store.sort(this.view.columns[index].dataIndex);
44608         }
44609     }
44610 });
44611
44612 // Backwards compatibility alias
44613 Ext.ListView.Sorter = Ext.list.Sorter;/**
44614  * @class Ext.TabPanel
44615  * <p>A basic tab container. TabPanels can be used exactly like a standard {@link Ext.Panel}
44616  * for layout purposes, but also have special support for containing child Components
44617  * (<tt>{@link Ext.Container#items items}</tt>) that are managed using a
44618  * {@link Ext.layout.CardLayout CardLayout layout manager}, and displayed as separate tabs.</p>
44619  *
44620  * <b>Note:</b> By default, a tab's close tool <i>destroys</i> the child tab Component
44621  * and all its descendants. This makes the child tab Component, and all its descendants <b>unusable</b>. To enable
44622  * re-use of a tab, configure the TabPanel with <b><code>{@link #autoDestroy autoDestroy: false}</code></b>.
44623  *
44624  * <p><b><u>TabPanel header/footer elements</u></b></p>
44625  * <p>TabPanels use their {@link Ext.Panel#header header} or {@link Ext.Panel#footer footer} element
44626  * (depending on the {@link #tabPosition} configuration) to accommodate the tab selector buttons.
44627  * This means that a TabPanel will not display any configured title, and will not display any
44628  * configured header {@link Ext.Panel#tools tools}.</p>
44629  * <p>To display a header, embed the TabPanel in a {@link Ext.Panel Panel} which uses
44630  * <b><tt>{@link Ext.Container#layout layout:'fit'}</tt></b>.</p>
44631  *
44632  * <p><b><u>Tab Events</u></b></p>
44633  * <p>There is no actual tab class &mdash; each tab is simply a {@link Ext.BoxComponent Component}
44634  * such as a {@link Ext.Panel Panel}. However, when rendered in a TabPanel, each child Component
44635  * can fire additional events that only exist for tabs and are not available from other Components.
44636  * These events are:</p>
44637  * <div><ul class="mdetail-params">
44638  * <li><tt><b>{@link Ext.Panel#activate activate}</b></tt> : Fires when this Component becomes
44639  * the active tab.</li>
44640  * <li><tt><b>{@link Ext.Panel#deactivate deactivate}</b></tt> : Fires when the Component that
44641  * was the active tab becomes deactivated.</li>
44642  * <li><tt><b>{@link Ext.Panel#beforeclose beforeclose}</b></tt> : Fires when the user clicks on the close tool of a closeable tab.
44643  * May be vetoed by returning <code>false</code> from a handler.</li>
44644  * <li><tt><b>{@link Ext.Panel#close close}</b></tt> : Fires a closeable tab has been closed by the user.</li>
44645  * </ul></div>
44646  * <p><b><u>Creating TabPanels from Code</u></b></p>
44647  * <p>TabPanels can be created and rendered completely in code, as in this example:</p>
44648  * <pre><code>
44649 var tabs = new Ext.TabPanel({
44650     renderTo: Ext.getBody(),
44651     activeTab: 0,
44652     items: [{
44653         title: 'Tab 1',
44654         html: 'A simple tab'
44655     },{
44656         title: 'Tab 2',
44657         html: 'Another one'
44658     }]
44659 });
44660 </code></pre>
44661  * <p><b><u>Creating TabPanels from Existing Markup</u></b></p>
44662  * <p>TabPanels can also be rendered from pre-existing markup in a couple of ways.</p>
44663  * <div><ul class="mdetail-params">
44664  *
44665  * <li>Pre-Structured Markup</li>
44666  * <div class="sub-desc">
44667  * <p>A container div with one or more nested tab divs with class <tt>'x-tab'</tt> can be rendered entirely
44668  * from existing markup (See the {@link #autoTabs} example).</p>
44669  * </div>
44670  *
44671  * <li>Un-Structured Markup</li>
44672  * <div class="sub-desc">
44673  * <p>A TabPanel can also be rendered from markup that is not strictly structured by simply specifying by id
44674  * which elements should be the container and the tabs. Using this method tab content can be pulled from different
44675  * elements within the page by id regardless of page structure. For example:</p>
44676  * <pre><code>
44677 var tabs = new Ext.TabPanel({
44678     renderTo: 'my-tabs',
44679     activeTab: 0,
44680     items:[
44681         {contentEl:'tab1', title:'Tab 1'},
44682         {contentEl:'tab2', title:'Tab 2'}
44683     ]
44684 });
44685
44686 // Note that the tabs do not have to be nested within the container (although they can be)
44687 &lt;div id="my-tabs">&lt;/div>
44688 &lt;div id="tab1" class="x-hide-display">A simple tab&lt;/div>
44689 &lt;div id="tab2" class="x-hide-display">Another one&lt;/div>
44690 </code></pre>
44691  * Note that the tab divs in this example contain the class <tt>'x-hide-display'</tt> so that they can be rendered
44692  * deferred without displaying outside the tabs. You could alternately set <tt>{@link #deferredRender} = false </tt>
44693  * to render all content tabs on page load.
44694  * </div>
44695  *
44696  * </ul></div>
44697  *
44698  * @extends Ext.Panel
44699  * @constructor
44700  * @param {Object} config The configuration options
44701  * @xtype tabpanel
44702  */
44703 Ext.TabPanel = Ext.extend(Ext.Panel,  {
44704     /**
44705      * @cfg {Boolean} layoutOnTabChange
44706      * Set to true to force a layout of the active tab when the tab is changed. Defaults to false.
44707      * See {@link Ext.layout.CardLayout}.<code>{@link Ext.layout.CardLayout#layoutOnCardChange layoutOnCardChange}</code>.
44708      */
44709     /**
44710      * @cfg {String} tabCls <b>This config option is used on <u>child Components</u> of ths TabPanel.</b> A CSS
44711      * class name applied to the tab strip item representing the child Component, allowing special
44712      * styling to be applied.
44713      */
44714     /**
44715      * @cfg {Boolean} deferredRender
44716      * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.Container#items items}</tt>
44717      * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
44718      * <tt>{@link Ext.Container#items items}</tt> as soon as the {@link Ext.layout.CardLayout layout}
44719      * is rendered. If there is a significant amount of content or a lot of heavy controls being
44720      * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
44721      * improve performance.</p>
44722      * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
44723      * TabPanels ({@link Ext.layout.CardLayout}) as its {@link Ext.layout.CardLayout#deferredRender}
44724      * configuration value.</p>
44725      * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
44726      * within an unactivated tab will not be available. For example, this means that if the TabPanel
44727      * is within a {@link Ext.form.FormPanel form}, then until a tab is activated, any Fields within
44728      * unactivated tabs will not be rendered, and will therefore not be submitted and will not be
44729      * available to either {@link Ext.form.BasicForm#getValues getValues} or
44730      * {@link Ext.form.BasicForm#setValues setValues}.</p>
44731      */
44732     deferredRender : true,
44733     /**
44734      * @cfg {Number} tabWidth The initial width in pixels of each new tab (defaults to 120).
44735      */
44736     tabWidth : 120,
44737     /**
44738      * @cfg {Number} minTabWidth The minimum width in pixels for each tab when {@link #resizeTabs} = true (defaults to 30).
44739      */
44740     minTabWidth : 30,
44741     /**
44742      * @cfg {Boolean} resizeTabs True to automatically resize each tab so that the tabs will completely fill the
44743      * tab strip (defaults to false).  Setting this to true may cause specific widths that might be set per tab to
44744      * be overridden in order to fit them all into view (although {@link #minTabWidth} will always be honored).
44745      */
44746     resizeTabs : false,
44747     /**
44748      * @cfg {Boolean} enableTabScroll True to enable scrolling to tabs that may be invisible due to overflowing the
44749      * overall TabPanel width. Only available with tabPosition:'top' (defaults to false).
44750      */
44751     enableTabScroll : false,
44752     /**
44753      * @cfg {Number} scrollIncrement The number of pixels to scroll each time a tab scroll button is pressed
44754      * (defaults to <tt>100</tt>, or if <tt>{@link #resizeTabs} = true</tt>, the calculated tab width).  Only
44755      * applies when <tt>{@link #enableTabScroll} = true</tt>.
44756      */
44757     scrollIncrement : 0,
44758     /**
44759      * @cfg {Number} scrollRepeatInterval Number of milliseconds between each scroll while a tab scroll button is
44760      * continuously pressed (defaults to <tt>400</tt>).
44761      */
44762     scrollRepeatInterval : 400,
44763     /**
44764      * @cfg {Float} scrollDuration The number of milliseconds that each scroll animation should last (defaults
44765      * to <tt>.35</tt>). Only applies when <tt>{@link #animScroll} = true</tt>.
44766      */
44767     scrollDuration : 0.35,
44768     /**
44769      * @cfg {Boolean} animScroll True to animate tab scrolling so that hidden tabs slide smoothly into view (defaults
44770      * to <tt>true</tt>).  Only applies when <tt>{@link #enableTabScroll} = true</tt>.
44771      */
44772     animScroll : true,
44773     /**
44774      * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <tt>'top'</tt>).
44775      * The only other supported value is <tt>'bottom'</tt>.  <b>Note</b>: tab scrolling is only supported for
44776      * <tt>tabPosition: 'top'</tt>.
44777      */
44778     tabPosition : 'top',
44779     /**
44780      * @cfg {String} baseCls The base CSS class applied to the panel (defaults to <tt>'x-tab-panel'</tt>).
44781      */
44782     baseCls : 'x-tab-panel',
44783     /**
44784      * @cfg {Boolean} autoTabs
44785      * <p><tt>true</tt> to query the DOM for any divs with a class of 'x-tab' to be automatically converted
44786      * to tabs and added to this panel (defaults to <tt>false</tt>).  Note that the query will be executed within
44787      * the scope of the container element only (so that multiple tab panels from markup can be supported via this
44788      * method).</p>
44789      * <p>This method is only possible when the markup is structured correctly as a container with nested divs
44790      * containing the class <tt>'x-tab'</tt>. To create TabPanels without these limitations, or to pull tab content
44791      * from other elements on the page, see the example at the top of the class for generating tabs from markup.</p>
44792      * <p>There are a couple of things to note when using this method:<ul>
44793      * <li>When using the <tt>autoTabs</tt> config (as opposed to passing individual tab configs in the TabPanel's
44794      * {@link #items} collection), you must use <tt>{@link #applyTo}</tt> to correctly use the specified <tt>id</tt>
44795      * as the tab container. The <tt>autoTabs</tt> method <em>replaces</em> existing content with the TabPanel
44796      * components.</li>
44797      * <li>Make sure that you set <tt>{@link #deferredRender}: false</tt> so that the content elements for each
44798      * tab will be rendered into the TabPanel immediately upon page load, otherwise they will not be transformed
44799      * until each tab is activated and will be visible outside the TabPanel.</li>
44800      * </ul>Example usage:</p>
44801      * <pre><code>
44802 var tabs = new Ext.TabPanel({
44803     applyTo: 'my-tabs',
44804     activeTab: 0,
44805     deferredRender: false,
44806     autoTabs: true
44807 });
44808
44809 // This markup will be converted to a TabPanel from the code above
44810 &lt;div id="my-tabs">
44811     &lt;div class="x-tab" title="Tab 1">A simple tab&lt;/div>
44812     &lt;div class="x-tab" title="Tab 2">Another one&lt;/div>
44813 &lt;/div>
44814 </code></pre>
44815      */
44816     autoTabs : false,
44817     /**
44818      * @cfg {String} autoTabSelector The CSS selector used to search for tabs in existing markup when
44819      * <tt>{@link #autoTabs} = true</tt> (defaults to <tt>'div.x-tab'</tt>).  This can be any valid selector
44820      * supported by {@link Ext.DomQuery#select}. Note that the query will be executed within the scope of this
44821      * tab panel only (so that multiple tab panels from markup can be supported on a page).
44822      */
44823     autoTabSelector : 'div.x-tab',
44824     /**
44825      * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially
44826      * activated on render (defaults to undefined).
44827      */
44828     activeTab : undefined,
44829     /**
44830      * @cfg {Number} tabMargin The number of pixels of space to calculate into the sizing and scrolling of
44831      * tabs. If you change the margin in CSS, you will need to update this value so calculations are correct
44832      * with either <tt>{@link #resizeTabs}</tt> or scrolling tabs. (defaults to <tt>2</tt>)
44833      */
44834     tabMargin : 2,
44835     /**
44836      * @cfg {Boolean} plain </tt>true</tt> to render the tab strip without a background container image
44837      * (defaults to <tt>false</tt>).
44838      */
44839     plain : false,
44840     /**
44841      * @cfg {Number} wheelIncrement For scrolling tabs, the number of pixels to increment on mouse wheel
44842      * scrolling (defaults to <tt>20</tt>).
44843      */
44844     wheelIncrement : 20,
44845
44846     /*
44847      * This is a protected property used when concatenating tab ids to the TabPanel id for internal uniqueness.
44848      * It does not generally need to be changed, but can be if external code also uses an id scheme that can
44849      * potentially clash with this one.
44850      */
44851     idDelimiter : '__',
44852
44853     // private
44854     itemCls : 'x-tab-item',
44855
44856     // private config overrides
44857     elements : 'body',
44858     headerAsText : false,
44859     frame : false,
44860     hideBorders :true,
44861
44862     // private
44863     initComponent : function(){
44864         this.frame = false;
44865         Ext.TabPanel.superclass.initComponent.call(this);
44866         this.addEvents(
44867             /**
44868              * @event beforetabchange
44869              * Fires before the active tab changes. Handlers can <tt>return false</tt> to cancel the tab change.
44870              * @param {TabPanel} this
44871              * @param {Panel} newTab The tab being activated
44872              * @param {Panel} currentTab The current active tab
44873              */
44874             'beforetabchange',
44875             /**
44876              * @event tabchange
44877              * Fires after the active tab has changed.
44878              * @param {TabPanel} this
44879              * @param {Panel} tab The new active tab
44880              */
44881             'tabchange',
44882             /**
44883              * @event contextmenu
44884              * Relays the contextmenu event from a tab selector element in the tab strip.
44885              * @param {TabPanel} this
44886              * @param {Panel} tab The target tab
44887              * @param {EventObject} e
44888              */
44889             'contextmenu'
44890         );
44891         /**
44892          * @cfg {Object} layoutConfig
44893          * TabPanel implicitly uses {@link Ext.layout.CardLayout} as its layout manager.
44894          * <code>layoutConfig</code> may be used to configure this layout manager.
44895          * <code>{@link #deferredRender}</code> and <code>{@link #layoutOnTabChange}</code>
44896          * configured on the TabPanel will be applied as configs to the layout manager.
44897          */
44898         this.setLayout(new Ext.layout.CardLayout(Ext.apply({
44899             layoutOnCardChange: this.layoutOnTabChange,
44900             deferredRender: this.deferredRender
44901         }, this.layoutConfig)));
44902
44903         if(this.tabPosition == 'top'){
44904             this.elements += ',header';
44905             this.stripTarget = 'header';
44906         }else {
44907             this.elements += ',footer';
44908             this.stripTarget = 'footer';
44909         }
44910         if(!this.stack){
44911             this.stack = Ext.TabPanel.AccessStack();
44912         }
44913         this.initItems();
44914     },
44915
44916     // private
44917     onRender : function(ct, position){
44918         Ext.TabPanel.superclass.onRender.call(this, ct, position);
44919
44920         if(this.plain){
44921             var pos = this.tabPosition == 'top' ? 'header' : 'footer';
44922             this[pos].addClass('x-tab-panel-'+pos+'-plain');
44923         }
44924
44925         var st = this[this.stripTarget];
44926
44927         this.stripWrap = st.createChild({cls:'x-tab-strip-wrap', cn:{
44928             tag:'ul', cls:'x-tab-strip x-tab-strip-'+this.tabPosition}});
44929
44930         var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);
44931         st.createChild({cls:'x-tab-strip-spacer'}, beforeEl);
44932         this.strip = new Ext.Element(this.stripWrap.dom.firstChild);
44933
44934         // create an empty span with class x-tab-strip-text to force the height of the header element when there's no tabs.
44935         this.edge = this.strip.createChild({tag:'li', cls:'x-tab-edge', cn: [{tag: 'span', cls: 'x-tab-strip-text', cn: '&#160;'}]});
44936         this.strip.createChild({cls:'x-clear'});
44937
44938         this.body.addClass('x-tab-panel-body-'+this.tabPosition);
44939
44940         /**
44941          * @cfg {Template/XTemplate} itemTpl <p>(Optional) A {@link Ext.Template Template} or
44942          * {@link Ext.XTemplate XTemplate} which may be provided to process the data object returned from
44943          * <tt>{@link #getTemplateArgs}</tt> to produce a clickable selector element in the tab strip.</p>
44944          * <p>The main element created should be a <tt>&lt;li></tt> element. In order for a click event on
44945          * a selector element to be connected to its item, it must take its <i>id</i> from the TabPanel's
44946          * native <tt>{@link #getTemplateArgs}</tt>.</p>
44947          * <p>The child element which contains the title text must be marked by the CSS class
44948          * <tt>x-tab-strip-inner</tt>.</p>
44949          * <p>To enable closability, the created element should contain an element marked by the CSS class
44950          * <tt>x-tab-strip-close</tt>.</p>
44951          * <p>If a custom <tt>itemTpl</tt> is supplied, it is the developer's responsibility to create CSS
44952          * style rules to create the desired appearance.</p>
44953          * Below is an example of how to create customized tab selector items:<pre><code>
44954 new Ext.TabPanel({
44955     renderTo: document.body,
44956     minTabWidth: 115,
44957     tabWidth: 135,
44958     enableTabScroll: true,
44959     width: 600,
44960     height: 250,
44961     defaults: {autoScroll:true},
44962     itemTpl: new Ext.XTemplate(
44963     '&lt;li class="{cls}" id="{id}" style="overflow:hidden">',
44964          '&lt;tpl if="closable">',
44965             '&lt;a class="x-tab-strip-close">&lt;/a>',
44966          '&lt;/tpl>',
44967          '&lt;a class="x-tab-right" href="#" style="padding-left:6px">',
44968             '&lt;em class="x-tab-left">',
44969                 '&lt;span class="x-tab-strip-inner">',
44970                     '&lt;img src="{src}" style="float:left;margin:3px 3px 0 0">',
44971                     '&lt;span style="margin-left:20px" class="x-tab-strip-text {iconCls}">{text} {extra}&lt;/span>',
44972                 '&lt;/span>',
44973             '&lt;/em>',
44974         '&lt;/a>',
44975     '&lt;/li>'
44976     ),
44977     getTemplateArgs: function(item) {
44978 //      Call the native method to collect the base data. Like the ID!
44979         var result = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);
44980
44981 //      Add stuff used in our template
44982         return Ext.apply(result, {
44983             closable: item.closable,
44984             src: item.iconSrc,
44985             extra: item.extraText || ''
44986         });
44987     },
44988     items: [{
44989         title: 'New Tab 1',
44990         iconSrc: '../shared/icons/fam/grid.png',
44991         html: 'Tab Body 1',
44992         closable: true
44993     }, {
44994         title: 'New Tab 2',
44995         iconSrc: '../shared/icons/fam/grid.png',
44996         html: 'Tab Body 2',
44997         extraText: 'Extra stuff in the tab button'
44998     }]
44999 });
45000 </code></pre>
45001          */
45002         if(!this.itemTpl){
45003             var tt = new Ext.Template(
45004                  '<li class="{cls}" id="{id}"><a class="x-tab-strip-close"></a>',
45005                  '<a class="x-tab-right" href="#"><em class="x-tab-left">',
45006                  '<span class="x-tab-strip-inner"><span class="x-tab-strip-text {iconCls}">{text}</span></span>',
45007                  '</em></a></li>'
45008             );
45009             tt.disableFormats = true;
45010             tt.compile();
45011             Ext.TabPanel.prototype.itemTpl = tt;
45012         }
45013
45014         this.items.each(this.initTab, this);
45015     },
45016
45017     // private
45018     afterRender : function(){
45019         Ext.TabPanel.superclass.afterRender.call(this);
45020         if(this.autoTabs){
45021             this.readTabs(false);
45022         }
45023         if(this.activeTab !== undefined){
45024             var item = Ext.isObject(this.activeTab) ? this.activeTab : this.items.get(this.activeTab);
45025             delete this.activeTab;
45026             this.setActiveTab(item);
45027         }
45028     },
45029
45030     // private
45031     initEvents : function(){
45032         Ext.TabPanel.superclass.initEvents.call(this);
45033         this.mon(this.strip, {
45034             scope: this,
45035             mousedown: this.onStripMouseDown,
45036             contextmenu: this.onStripContextMenu
45037         });
45038         if(this.enableTabScroll){
45039             this.mon(this.strip, 'mousewheel', this.onWheel, this);
45040         }
45041     },
45042
45043     // private
45044     findTargets : function(e){
45045         var item = null,
45046             itemEl = e.getTarget('li:not(.x-tab-edge)', this.strip);
45047
45048         if(itemEl){
45049             item = this.getComponent(itemEl.id.split(this.idDelimiter)[1]);
45050             if(item.disabled){
45051                 return {
45052                     close : null,
45053                     item : null,
45054                     el : null
45055                 };
45056             }
45057         }
45058         return {
45059             close : e.getTarget('.x-tab-strip-close', this.strip),
45060             item : item,
45061             el : itemEl
45062         };
45063     },
45064
45065     // private
45066     onStripMouseDown : function(e){
45067         if(e.button !== 0){
45068             return;
45069         }
45070         e.preventDefault();
45071         var t = this.findTargets(e);
45072         if(t.close){
45073             if (t.item.fireEvent('beforeclose', t.item) !== false) {
45074                 t.item.fireEvent('close', t.item);
45075                 this.remove(t.item);
45076             }
45077             return;
45078         }
45079         if(t.item && t.item != this.activeTab){
45080             this.setActiveTab(t.item);
45081         }
45082     },
45083
45084     // private
45085     onStripContextMenu : function(e){
45086         e.preventDefault();
45087         var t = this.findTargets(e);
45088         if(t.item){
45089             this.fireEvent('contextmenu', this, t.item, e);
45090         }
45091     },
45092
45093     /**
45094      * True to scan the markup in this tab panel for <tt>{@link #autoTabs}</tt> using the
45095      * <tt>{@link #autoTabSelector}</tt>
45096      * @param {Boolean} removeExisting True to remove existing tabs
45097      */
45098     readTabs : function(removeExisting){
45099         if(removeExisting === true){
45100             this.items.each(function(item){
45101                 this.remove(item);
45102             }, this);
45103         }
45104         var tabs = this.el.query(this.autoTabSelector);
45105         for(var i = 0, len = tabs.length; i < len; i++){
45106             var tab = tabs[i],
45107                 title = tab.getAttribute('title');
45108             tab.removeAttribute('title');
45109             this.add({
45110                 title: title,
45111                 contentEl: tab
45112             });
45113         }
45114     },
45115
45116     // private
45117     initTab : function(item, index){
45118         var before = this.strip.dom.childNodes[index],
45119             p = this.getTemplateArgs(item),
45120             el = before ?
45121                  this.itemTpl.insertBefore(before, p) :
45122                  this.itemTpl.append(this.strip, p),
45123             cls = 'x-tab-strip-over',
45124             tabEl = Ext.get(el);
45125
45126         tabEl.hover(function(){
45127             if(!item.disabled){
45128                 tabEl.addClass(cls);
45129             }
45130         }, function(){
45131             tabEl.removeClass(cls);
45132         });
45133
45134         if(item.tabTip){
45135             tabEl.child('span.x-tab-strip-text', true).qtip = item.tabTip;
45136         }
45137         item.tabEl = el;
45138
45139         // Route *keyboard triggered* click events to the tab strip mouse handler.
45140         tabEl.select('a').on('click', function(e){
45141             if(!e.getPageX()){
45142                 this.onStripMouseDown(e);
45143             }
45144         }, this, {preventDefault: true});
45145
45146         item.on({
45147             scope: this,
45148             disable: this.onItemDisabled,
45149             enable: this.onItemEnabled,
45150             titlechange: this.onItemTitleChanged,
45151             iconchange: this.onItemIconChanged,
45152             beforeshow: this.onBeforeShowItem
45153         });
45154     },
45155
45156
45157
45158     /**
45159      * <p>Provides template arguments for rendering a tab selector item in the tab strip.</p>
45160      * <p>This method returns an object hash containing properties used by the TabPanel's <tt>{@link #itemTpl}</tt>
45161      * to create a formatted, clickable tab selector element. The properties which must be returned
45162      * are:</p><div class="mdetail-params"><ul>
45163      * <li><b>id</b> : String<div class="sub-desc">A unique identifier which links to the item</div></li>
45164      * <li><b>text</b> : String<div class="sub-desc">The text to display</div></li>
45165      * <li><b>cls</b> : String<div class="sub-desc">The CSS class name</div></li>
45166      * <li><b>iconCls</b> : String<div class="sub-desc">A CSS class to provide appearance for an icon.</div></li>
45167      * </ul></div>
45168      * @param {Ext.BoxComponent} item The {@link Ext.BoxComponent BoxComponent} for which to create a selector element in the tab strip.
45169      * @return {Object} An object hash containing the properties required to render the selector element.
45170      */
45171     getTemplateArgs : function(item) {
45172         var cls = item.closable ? 'x-tab-strip-closable' : '';
45173         if(item.disabled){
45174             cls += ' x-item-disabled';
45175         }
45176         if(item.iconCls){
45177             cls += ' x-tab-with-icon';
45178         }
45179         if(item.tabCls){
45180             cls += ' ' + item.tabCls;
45181         }
45182
45183         return {
45184             id: this.id + this.idDelimiter + item.getItemId(),
45185             text: item.title,
45186             cls: cls,
45187             iconCls: item.iconCls || ''
45188         };
45189     },
45190
45191     // private
45192     onAdd : function(c){
45193         Ext.TabPanel.superclass.onAdd.call(this, c);
45194         if(this.rendered){
45195             var items = this.items;
45196             this.initTab(c, items.indexOf(c));
45197             this.delegateUpdates();
45198         }
45199     },
45200
45201     // private
45202     onBeforeAdd : function(item){
45203         var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);
45204         if(existing){
45205             this.setActiveTab(item);
45206             return false;
45207         }
45208         Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);
45209         var es = item.elements;
45210         item.elements = es ? es.replace(',header', '') : es;
45211         item.border = (item.border === true);
45212     },
45213
45214     // private
45215     onRemove : function(c){
45216         var te = Ext.get(c.tabEl);
45217         // check if the tabEl exists, it won't if the tab isn't rendered
45218         if(te){
45219             te.select('a').removeAllListeners();
45220             Ext.destroy(te);
45221         }
45222         Ext.TabPanel.superclass.onRemove.call(this, c);
45223         this.stack.remove(c);
45224         delete c.tabEl;
45225         c.un('disable', this.onItemDisabled, this);
45226         c.un('enable', this.onItemEnabled, this);
45227         c.un('titlechange', this.onItemTitleChanged, this);
45228         c.un('iconchange', this.onItemIconChanged, this);
45229         c.un('beforeshow', this.onBeforeShowItem, this);
45230         if(c == this.activeTab){
45231             var next = this.stack.next();
45232             if(next){
45233                 this.setActiveTab(next);
45234             }else if(this.items.getCount() > 0){
45235                 this.setActiveTab(0);
45236             }else{
45237                 this.setActiveTab(null);
45238             }
45239         }
45240         if(!this.destroying){
45241             this.delegateUpdates();
45242         }
45243     },
45244
45245     // private
45246     onBeforeShowItem : function(item){
45247         if(item != this.activeTab){
45248             this.setActiveTab(item);
45249             return false;
45250         }
45251     },
45252
45253     // private
45254     onItemDisabled : function(item){
45255         var el = this.getTabEl(item);
45256         if(el){
45257             Ext.fly(el).addClass('x-item-disabled');
45258         }
45259         this.stack.remove(item);
45260     },
45261
45262     // private
45263     onItemEnabled : function(item){
45264         var el = this.getTabEl(item);
45265         if(el){
45266             Ext.fly(el).removeClass('x-item-disabled');
45267         }
45268     },
45269
45270     // private
45271     onItemTitleChanged : function(item){
45272         var el = this.getTabEl(item);
45273         if(el){
45274             Ext.fly(el).child('span.x-tab-strip-text', true).innerHTML = item.title;
45275         }
45276     },
45277
45278     //private
45279     onItemIconChanged : function(item, iconCls, oldCls){
45280         var el = this.getTabEl(item);
45281         if(el){
45282             el = Ext.get(el);
45283             el.child('span.x-tab-strip-text').replaceClass(oldCls, iconCls);
45284             el[Ext.isEmpty(iconCls) ? 'removeClass' : 'addClass']('x-tab-with-icon');
45285         }
45286     },
45287
45288     /**
45289      * Gets the DOM element for the tab strip item which activates the child panel with the specified
45290      * ID. Access this to change the visual treatment of the item, for example by changing the CSS class name.
45291      * @param {Panel/Number/String} tab The tab component, or the tab's index, or the tabs id or itemId.
45292      * @return {HTMLElement} The DOM node
45293      */
45294     getTabEl : function(item){
45295         var c = this.getComponent(item);
45296         return c ? c.tabEl : null;
45297     },
45298
45299     // private
45300     onResize : function(){
45301         Ext.TabPanel.superclass.onResize.apply(this, arguments);
45302         this.delegateUpdates();
45303     },
45304
45305     /**
45306      * Suspends any internal calculations or scrolling while doing a bulk operation. See {@link #endUpdate}
45307      */
45308     beginUpdate : function(){
45309         this.suspendUpdates = true;
45310     },
45311
45312     /**
45313      * Resumes calculations and scrolling at the end of a bulk operation. See {@link #beginUpdate}
45314      */
45315     endUpdate : function(){
45316         this.suspendUpdates = false;
45317         this.delegateUpdates();
45318     },
45319
45320     /**
45321      * Hides the tab strip item for the passed tab
45322      * @param {Number/String/Panel} item The tab index, id or item
45323      */
45324     hideTabStripItem : function(item){
45325         item = this.getComponent(item);
45326         var el = this.getTabEl(item);
45327         if(el){
45328             el.style.display = 'none';
45329             this.delegateUpdates();
45330         }
45331         this.stack.remove(item);
45332     },
45333
45334     /**
45335      * Unhides the tab strip item for the passed tab
45336      * @param {Number/String/Panel} item The tab index, id or item
45337      */
45338     unhideTabStripItem : function(item){
45339         item = this.getComponent(item);
45340         var el = this.getTabEl(item);
45341         if(el){
45342             el.style.display = '';
45343             this.delegateUpdates();
45344         }
45345     },
45346
45347     // private
45348     delegateUpdates : function(){
45349         if(this.suspendUpdates){
45350             return;
45351         }
45352         if(this.resizeTabs && this.rendered){
45353             this.autoSizeTabs();
45354         }
45355         if(this.enableTabScroll && this.rendered){
45356             this.autoScrollTabs();
45357         }
45358     },
45359
45360     // private
45361     autoSizeTabs : function(){
45362         var count = this.items.length,
45363             ce = this.tabPosition != 'bottom' ? 'header' : 'footer',
45364             ow = this[ce].dom.offsetWidth,
45365             aw = this[ce].dom.clientWidth;
45366
45367         if(!this.resizeTabs || count < 1 || !aw){ // !aw for display:none
45368             return;
45369         }
45370
45371         var each = Math.max(Math.min(Math.floor((aw-4) / count) - this.tabMargin, this.tabWidth), this.minTabWidth); // -4 for float errors in IE
45372         this.lastTabWidth = each;
45373         var lis = this.strip.query('li:not(.x-tab-edge)');
45374         for(var i = 0, len = lis.length; i < len; i++) {
45375             var li = lis[i],
45376                 inner = Ext.fly(li).child('.x-tab-strip-inner', true),
45377                 tw = li.offsetWidth,
45378                 iw = inner.offsetWidth;
45379             inner.style.width = (each - (tw-iw)) + 'px';
45380         }
45381     },
45382
45383     // private
45384     adjustBodyWidth : function(w){
45385         if(this.header){
45386             this.header.setWidth(w);
45387         }
45388         if(this.footer){
45389             this.footer.setWidth(w);
45390         }
45391         return w;
45392     },
45393
45394     /**
45395      * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which
45396      * can <tt>return false</tt> to cancel the tab change.
45397      * @param {String/Number} item
45398      * The id or tab Panel to activate. This parameter may be any of the following:
45399      * <div><ul class="mdetail-params">
45400      * <li>a <b><tt>String</tt></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
45401      * or <code>{@link Ext.Component#id id}</code> of the child component </li>
45402      * <li>a <b><tt>Number</tt></b> : representing the position of the child component
45403      * within the <code>{@link Ext.Container#items items}</code> <b>property</b></li>
45404      * </ul></div>
45405      * <p>For additional information see {@link Ext.util.MixedCollection#get}.
45406      */
45407     setActiveTab : function(item){
45408         item = this.getComponent(item);
45409         if(this.fireEvent('beforetabchange', this, item, this.activeTab) === false){
45410             return;
45411         }
45412         if(!this.rendered){
45413             this.activeTab = item;
45414             return;
45415         }
45416         if(this.activeTab != item){
45417             if(this.activeTab){
45418                 var oldEl = this.getTabEl(this.activeTab);
45419                 if(oldEl){
45420                     Ext.fly(oldEl).removeClass('x-tab-strip-active');
45421                 }
45422             }
45423             this.activeTab = item;
45424             if(item){
45425                 var el = this.getTabEl(item);
45426                 Ext.fly(el).addClass('x-tab-strip-active');
45427                 this.stack.add(item);
45428
45429                 this.layout.setActiveItem(item);
45430                 if(this.scrolling){
45431                     this.scrollToTab(item, this.animScroll);
45432                 }
45433             }
45434             this.fireEvent('tabchange', this, item);
45435         }
45436     },
45437
45438     /**
45439      * Returns the Component which is the currently active tab. <b>Note that before the TabPanel
45440      * first activates a child Component, this method will return whatever was configured in the
45441      * {@link #activeTab} config option.</b>
45442      * @return {BoxComponent} The currently active child Component if one <i>is</i> active, or the {@link #activeTab} config value.
45443      */
45444     getActiveTab : function(){
45445         return this.activeTab || null;
45446     },
45447
45448     /**
45449      * Gets the specified tab by id.
45450      * @param {String} id The tab id
45451      * @return {Panel} The tab
45452      */
45453     getItem : function(item){
45454         return this.getComponent(item);
45455     },
45456
45457     // private
45458     autoScrollTabs : function(){
45459         this.pos = this.tabPosition=='bottom' ? this.footer : this.header;
45460         var count = this.items.length,
45461             ow = this.pos.dom.offsetWidth,
45462             tw = this.pos.dom.clientWidth,
45463             wrap = this.stripWrap,
45464             wd = wrap.dom,
45465             cw = wd.offsetWidth,
45466             pos = this.getScrollPos(),
45467             l = this.edge.getOffsetsTo(this.stripWrap)[0] + pos;
45468
45469         if(!this.enableTabScroll || count < 1 || cw < 20){ // 20 to prevent display:none issues
45470             return;
45471         }
45472         if(l <= tw){
45473             wd.scrollLeft = 0;
45474             wrap.setWidth(tw);
45475             if(this.scrolling){
45476                 this.scrolling = false;
45477                 this.pos.removeClass('x-tab-scrolling');
45478                 this.scrollLeft.hide();
45479                 this.scrollRight.hide();
45480                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
45481                 if(Ext.isAir || Ext.isWebKit){
45482                     wd.style.marginLeft = '';
45483                     wd.style.marginRight = '';
45484                 }
45485             }
45486         }else{
45487             if(!this.scrolling){
45488                 this.pos.addClass('x-tab-scrolling');
45489                 // See here: http://extjs.com/forum/showthread.php?t=49308&highlight=isSafari
45490                 if(Ext.isAir || Ext.isWebKit){
45491                     wd.style.marginLeft = '18px';
45492                     wd.style.marginRight = '18px';
45493                 }
45494             }
45495             tw -= wrap.getMargins('lr');
45496             wrap.setWidth(tw > 20 ? tw : 20);
45497             if(!this.scrolling){
45498                 if(!this.scrollLeft){
45499                     this.createScrollers();
45500                 }else{
45501                     this.scrollLeft.show();
45502                     this.scrollRight.show();
45503                 }
45504             }
45505             this.scrolling = true;
45506             if(pos > (l-tw)){ // ensure it stays within bounds
45507                 wd.scrollLeft = l-tw;
45508             }else{ // otherwise, make sure the active tab is still visible
45509                 this.scrollToTab(this.activeTab, false);
45510             }
45511             this.updateScrollButtons();
45512         }
45513     },
45514
45515     // private
45516     createScrollers : function(){
45517         this.pos.addClass('x-tab-scrolling-' + this.tabPosition);
45518         var h = this.stripWrap.dom.offsetHeight;
45519
45520         // left
45521         var sl = this.pos.insertFirst({
45522             cls:'x-tab-scroller-left'
45523         });
45524         sl.setHeight(h);
45525         sl.addClassOnOver('x-tab-scroller-left-over');
45526         this.leftRepeater = new Ext.util.ClickRepeater(sl, {
45527             interval : this.scrollRepeatInterval,
45528             handler: this.onScrollLeft,
45529             scope: this
45530         });
45531         this.scrollLeft = sl;
45532
45533         // right
45534         var sr = this.pos.insertFirst({
45535             cls:'x-tab-scroller-right'
45536         });
45537         sr.setHeight(h);
45538         sr.addClassOnOver('x-tab-scroller-right-over');
45539         this.rightRepeater = new Ext.util.ClickRepeater(sr, {
45540             interval : this.scrollRepeatInterval,
45541             handler: this.onScrollRight,
45542             scope: this
45543         });
45544         this.scrollRight = sr;
45545     },
45546
45547     // private
45548     getScrollWidth : function(){
45549         return this.edge.getOffsetsTo(this.stripWrap)[0] + this.getScrollPos();
45550     },
45551
45552     // private
45553     getScrollPos : function(){
45554         return parseInt(this.stripWrap.dom.scrollLeft, 10) || 0;
45555     },
45556
45557     // private
45558     getScrollArea : function(){
45559         return parseInt(this.stripWrap.dom.clientWidth, 10) || 0;
45560     },
45561
45562     // private
45563     getScrollAnim : function(){
45564         return {duration:this.scrollDuration, callback: this.updateScrollButtons, scope: this};
45565     },
45566
45567     // private
45568     getScrollIncrement : function(){
45569         return this.scrollIncrement || (this.resizeTabs ? this.lastTabWidth+2 : 100);
45570     },
45571
45572     /**
45573      * Scrolls to a particular tab if tab scrolling is enabled
45574      * @param {Panel} item The item to scroll to
45575      * @param {Boolean} animate True to enable animations
45576      */
45577
45578     scrollToTab : function(item, animate){
45579         if(!item){
45580             return;
45581         }
45582         var el = this.getTabEl(item),
45583             pos = this.getScrollPos(),
45584             area = this.getScrollArea(),
45585             left = Ext.fly(el).getOffsetsTo(this.stripWrap)[0] + pos,
45586             right = left + el.offsetWidth;
45587         if(left < pos){
45588             this.scrollTo(left, animate);
45589         }else if(right > (pos + area)){
45590             this.scrollTo(right - area, animate);
45591         }
45592     },
45593
45594     // private
45595     scrollTo : function(pos, animate){
45596         this.stripWrap.scrollTo('left', pos, animate ? this.getScrollAnim() : false);
45597         if(!animate){
45598             this.updateScrollButtons();
45599         }
45600     },
45601
45602     onWheel : function(e){
45603         var d = e.getWheelDelta()*this.wheelIncrement*-1;
45604         e.stopEvent();
45605
45606         var pos = this.getScrollPos(),
45607             newpos = pos + d,
45608             sw = this.getScrollWidth()-this.getScrollArea();
45609
45610         var s = Math.max(0, Math.min(sw, newpos));
45611         if(s != pos){
45612             this.scrollTo(s, false);
45613         }
45614     },
45615
45616     // private
45617     onScrollRight : function(){
45618         var sw = this.getScrollWidth()-this.getScrollArea(),
45619             pos = this.getScrollPos(),
45620             s = Math.min(sw, pos + this.getScrollIncrement());
45621         if(s != pos){
45622             this.scrollTo(s, this.animScroll);
45623         }
45624     },
45625
45626     // private
45627     onScrollLeft : function(){
45628         var pos = this.getScrollPos(),
45629             s = Math.max(0, pos - this.getScrollIncrement());
45630         if(s != pos){
45631             this.scrollTo(s, this.animScroll);
45632         }
45633     },
45634
45635     // private
45636     updateScrollButtons : function(){
45637         var pos = this.getScrollPos();
45638         this.scrollLeft[pos === 0 ? 'addClass' : 'removeClass']('x-tab-scroller-left-disabled');
45639         this.scrollRight[pos >= (this.getScrollWidth()-this.getScrollArea()) ? 'addClass' : 'removeClass']('x-tab-scroller-right-disabled');
45640     },
45641
45642     // private
45643     beforeDestroy : function() {
45644         Ext.destroy(this.leftRepeater, this.rightRepeater);
45645         this.deleteMembers('strip', 'edge', 'scrollLeft', 'scrollRight', 'stripWrap');
45646         this.activeTab = null;
45647         Ext.TabPanel.superclass.beforeDestroy.apply(this);
45648     }
45649
45650     /**
45651      * @cfg {Boolean} collapsible
45652      * @hide
45653      */
45654     /**
45655      * @cfg {String} header
45656      * @hide
45657      */
45658     /**
45659      * @cfg {Boolean} headerAsText
45660      * @hide
45661      */
45662     /**
45663      * @property header
45664      * @hide
45665      */
45666     /**
45667      * @cfg title
45668      * @hide
45669      */
45670     /**
45671      * @cfg {Array} tools
45672      * @hide
45673      */
45674     /**
45675      * @cfg {Array} toolTemplate
45676      * @hide
45677      */
45678     /**
45679      * @cfg {Boolean} hideCollapseTool
45680      * @hide
45681      */
45682     /**
45683      * @cfg {Boolean} titleCollapse
45684      * @hide
45685      */
45686     /**
45687      * @cfg {Boolean} collapsed
45688      * @hide
45689      */
45690     /**
45691      * @cfg {String} layout
45692      * @hide
45693      */
45694     /**
45695      * @cfg {Boolean} preventBodyReset
45696      * @hide
45697      */
45698 });
45699 Ext.reg('tabpanel', Ext.TabPanel);
45700
45701 /**
45702  * See {@link #setActiveTab}. Sets the specified tab as the active tab. This method fires
45703  * the {@link #beforetabchange} event which can <tt>return false</tt> to cancel the tab change.
45704  * @param {String/Panel} tab The id or tab Panel to activate
45705  * @method activate
45706  */
45707 Ext.TabPanel.prototype.activate = Ext.TabPanel.prototype.setActiveTab;
45708
45709 // private utility class used by TabPanel
45710 Ext.TabPanel.AccessStack = function(){
45711     var items = [];
45712     return {
45713         add : function(item){
45714             items.push(item);
45715             if(items.length > 10){
45716                 items.shift();
45717             }
45718         },
45719
45720         remove : function(item){
45721             var s = [];
45722             for(var i = 0, len = items.length; i < len; i++) {
45723                 if(items[i] != item){
45724                     s.push(items[i]);
45725                 }
45726             }
45727             items = s;
45728         },
45729
45730         next : function(){
45731             return items.pop();
45732         }
45733     };
45734 };
45735 /**
45736  * @class Ext.Button
45737  * @extends Ext.BoxComponent
45738  * Simple Button class
45739  * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
45740  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
45741  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
45742  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
45743  * The handler is passed the following parameters:<div class="mdetail-params"><ul>
45744  * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
45745  * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
45746  * </ul></div>
45747  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
45748  * See also {@link Ext.Panel}.<tt>{@link Ext.Panel#minButtonWidth minButtonWidth}</tt>.
45749  * @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
45750  * @cfg {Boolean} hidden True to start hidden (defaults to false)
45751  * @cfg {Boolean} disabled True to start disabled (defaults to false)
45752  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
45753  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
45754  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
45755  * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
45756  * @constructor
45757  * Create a new button
45758  * @param {Object} config The config object
45759  * @xtype button
45760  */
45761 Ext.Button = Ext.extend(Ext.BoxComponent, {
45762     /**
45763      * Read-only. True if this button is hidden
45764      * @type Boolean
45765      */
45766     hidden : false,
45767     /**
45768      * Read-only. True if this button is disabled
45769      * @type Boolean
45770      */
45771     disabled : false,
45772     /**
45773      * Read-only. True if this button is pressed (only if enableToggle = true)
45774      * @type Boolean
45775      */
45776     pressed : false,
45777
45778     /**
45779      * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
45780      */
45781
45782     /**
45783      * @cfg {Boolean} allowDepress
45784      * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
45785      */
45786
45787     /**
45788      * @cfg {Boolean} enableToggle
45789      * True to enable pressed/not pressed toggling (defaults to false)
45790      */
45791     enableToggle : false,
45792     /**
45793      * @cfg {Function} toggleHandler
45794      * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
45795      * <li><b>button</b> : Ext.Button<div class="sub-desc">this Button object</div></li>
45796      * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>
45797      * </ul>
45798      */
45799     /**
45800      * @cfg {Mixed} menu
45801      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
45802      */
45803     /**
45804      * @cfg {String} menuAlign
45805      * The position to align the menu to (see {@link Ext.Element#alignTo} for more details, defaults to 'tl-bl?').
45806      */
45807     menuAlign : 'tl-bl?',
45808
45809     /**
45810      * @cfg {String} overflowText If used in a {@link Ext.Toolbar Toolbar}, the
45811      * text to be used if this item is shown in the overflow menu. See also
45812      * {@link Ext.Toolbar.Item}.<code>{@link Ext.Toolbar.Item#overflowText overflowText}</code>.
45813      */
45814     /**
45815      * @cfg {String} iconCls
45816      * A css class which sets a background image to be used as the icon for this button
45817      */
45818     /**
45819      * @cfg {String} type
45820      * submit, reset or button - defaults to 'button'
45821      */
45822     type : 'button',
45823
45824     // private
45825     menuClassTarget : 'tr:nth(2)',
45826
45827     /**
45828      * @cfg {String} clickEvent
45829      * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
45830      * Defaults to <tt>'click'</tt>.
45831      */
45832     clickEvent : 'click',
45833
45834     /**
45835      * @cfg {Boolean} handleMouseEvents
45836      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
45837      */
45838     handleMouseEvents : true,
45839
45840     /**
45841      * @cfg {String} tooltipType
45842      * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
45843      */
45844     tooltipType : 'qtip',
45845
45846     /**
45847      * @cfg {String} buttonSelector
45848      * <p>(Optional) A {@link Ext.DomQuery DomQuery} selector which is used to extract the active, clickable element from the
45849      * DOM structure created.</p>
45850      * <p>When a custom {@link #template} is used, you  must ensure that this selector results in the selection of
45851      * a focussable element.</p>
45852      * <p>Defaults to <b><tt>'button:first-child'</tt></b>.</p>
45853      */
45854     buttonSelector : 'button:first-child',
45855
45856     /**
45857      * @cfg {String} scale
45858      * <p>(Optional) The size of the Button. Three values are allowed:</p>
45859      * <ul class="mdetail-params">
45860      * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
45861      * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
45862      * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
45863      * </ul>
45864      * <p>Defaults to <b><tt>'small'</tt></b>.</p>
45865      */
45866     scale : 'small',
45867
45868     /**
45869      * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
45870      * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
45871      * executed. Defaults to this Button.
45872      */
45873
45874     /**
45875      * @cfg {String} iconAlign
45876      * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
45877      * <ul class="mdetail-params">
45878      * <li>'top'<div class="sub-desc"></div></li>
45879      * <li>'right'<div class="sub-desc"></div></li>
45880      * <li>'bottom'<div class="sub-desc"></div></li>
45881      * <li>'left'<div class="sub-desc"></div></li>
45882      * </ul>
45883      * <p>Defaults to <b><tt>'left'</tt></b>.</p>
45884      */
45885     iconAlign : 'left',
45886
45887     /**
45888      * @cfg {String} arrowAlign
45889      * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
45890      * Two values are allowed:</p>
45891      * <ul class="mdetail-params">
45892      * <li>'right'<div class="sub-desc"></div></li>
45893      * <li>'bottom'<div class="sub-desc"></div></li>
45894      * </ul>
45895      * <p>Defaults to <b><tt>'right'</tt></b>.</p>
45896      */
45897     arrowAlign : 'right',
45898
45899     /**
45900      * @cfg {Ext.Template} template (Optional)
45901      * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
45902      * Instances, or subclasses which need a different DOM structure may provide a different
45903      * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
45904      * @type Ext.Template
45905      * @property template
45906      */
45907     /**
45908      * @cfg {String} cls
45909      * A CSS class string to apply to the button's main element.
45910      */
45911     /**
45912      * @property menu
45913      * @type Menu
45914      * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
45915      */
45916     /**
45917      * @cfg {Boolean} autoWidth
45918      * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content.
45919      * If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent
45920      * the button from doing this automatic sizing.
45921      * Defaults to <tt>undefined</tt>.
45922      */
45923
45924     initComponent : function(){
45925         Ext.Button.superclass.initComponent.call(this);
45926
45927         this.addEvents(
45928             /**
45929              * @event click
45930              * Fires when this button is clicked
45931              * @param {Button} this
45932              * @param {EventObject} e The click event
45933              */
45934             'click',
45935             /**
45936              * @event toggle
45937              * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
45938              * @param {Button} this
45939              * @param {Boolean} pressed
45940              */
45941             'toggle',
45942             /**
45943              * @event mouseover
45944              * Fires when the mouse hovers over the button
45945              * @param {Button} this
45946              * @param {Event} e The event object
45947              */
45948             'mouseover',
45949             /**
45950              * @event mouseout
45951              * Fires when the mouse exits the button
45952              * @param {Button} this
45953              * @param {Event} e The event object
45954              */
45955             'mouseout',
45956             /**
45957              * @event menushow
45958              * If this button has a menu, this event fires when it is shown
45959              * @param {Button} this
45960              * @param {Menu} menu
45961              */
45962             'menushow',
45963             /**
45964              * @event menuhide
45965              * If this button has a menu, this event fires when it is hidden
45966              * @param {Button} this
45967              * @param {Menu} menu
45968              */
45969             'menuhide',
45970             /**
45971              * @event menutriggerover
45972              * If this button has a menu, this event fires when the mouse enters the menu triggering element
45973              * @param {Button} this
45974              * @param {Menu} menu
45975              * @param {EventObject} e
45976              */
45977             'menutriggerover',
45978             /**
45979              * @event menutriggerout
45980              * If this button has a menu, this event fires when the mouse leaves the menu triggering element
45981              * @param {Button} this
45982              * @param {Menu} menu
45983              * @param {EventObject} e
45984              */
45985             'menutriggerout'
45986         );
45987         if(this.menu){
45988             this.menu = Ext.menu.MenuMgr.get(this.menu);
45989         }
45990         if(Ext.isString(this.toggleGroup)){
45991             this.enableToggle = true;
45992         }
45993     },
45994
45995 /**
45996   * <p>This method returns an Array which provides substitution parameters for the {@link #template Template} used
45997   * to create this Button's DOM structure.</p>
45998   * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
45999   * own implementation of this method.</p>
46000   * <p>The default implementation which provides data for the default {@link #template} returns an Array containing the
46001   * following items:</p><div class="mdetail-params"><ul>
46002   * <li>The &lt;button&gt;'s {@link #type}</li>
46003   * <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>
46004   * <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>
46005   * <li>The {@link #cls} CSS class name applied to the button's wrapping &lt;table&gt; element.</li>
46006   * <li>The Component id which is applied to the button's wrapping &lt;table&gt; element.</li>
46007   * </ul></div>
46008   * @return {Array} Substitution data for a Template.
46009  */
46010     getTemplateArgs : function(){
46011         return [this.type, 'x-btn-' + this.scale + ' x-btn-icon-' + this.scale + '-' + this.iconAlign, this.getMenuClass(), this.cls, this.id];
46012     },
46013
46014     // private
46015     setButtonClass : function(){
46016         if(this.useSetClass){
46017             if(!Ext.isEmpty(this.oldCls)){
46018                 this.el.removeClass([this.oldCls, 'x-btn-pressed']);
46019             }
46020             this.oldCls = (this.iconCls || this.icon) ? (this.text ? 'x-btn-text-icon' : 'x-btn-icon') : 'x-btn-noicon';
46021             this.el.addClass([this.oldCls, this.pressed ? 'x-btn-pressed' : null]);
46022         }
46023     },
46024
46025     // protected
46026     getMenuClass : function(){
46027         return this.menu ? (this.arrowAlign != 'bottom' ? 'x-btn-arrow' : 'x-btn-arrow-bottom') : '';
46028     },
46029
46030     // private
46031     onRender : function(ct, position){
46032         if(!this.template){
46033             if(!Ext.Button.buttonTemplate){
46034                 // hideous table template
46035                 Ext.Button.buttonTemplate = new Ext.Template(
46036                     '<table id="{4}" cellspacing="0" class="x-btn {3}"><tbody class="{1}">',
46037                     '<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>',
46038                     '<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>',
46039                     '<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>',
46040                     '</tbody></table>');
46041                 Ext.Button.buttonTemplate.compile();
46042             }
46043             this.template = Ext.Button.buttonTemplate;
46044         }
46045
46046         var btn, targs = this.getTemplateArgs();
46047
46048         if(position){
46049             btn = this.template.insertBefore(position, targs, true);
46050         }else{
46051             btn = this.template.append(ct, targs, true);
46052         }
46053         /**
46054          * An {@link Ext.Element Element} encapsulating the Button's clickable element. By default,
46055          * this references a <tt>&lt;button&gt;</tt> element. Read only.
46056          * @type Ext.Element
46057          * @property btnEl
46058          */
46059         this.btnEl = btn.child(this.buttonSelector);
46060         this.mon(this.btnEl, {
46061             scope: this,
46062             focus: this.onFocus,
46063             blur: this.onBlur
46064         });
46065
46066         this.initButtonEl(btn, this.btnEl);
46067
46068         Ext.ButtonToggleMgr.register(this);
46069     },
46070
46071     // private
46072     initButtonEl : function(btn, btnEl){
46073         this.el = btn;
46074         this.setIcon(this.icon);
46075         this.setText(this.text);
46076         this.setIconClass(this.iconCls);
46077         if(Ext.isDefined(this.tabIndex)){
46078             btnEl.dom.tabIndex = this.tabIndex;
46079         }
46080         if(this.tooltip){
46081             this.setTooltip(this.tooltip, true);
46082         }
46083
46084         if(this.handleMouseEvents){
46085             this.mon(btn, {
46086                 scope: this,
46087                 mouseover: this.onMouseOver,
46088                 mousedown: this.onMouseDown
46089             });
46090
46091             // new functionality for monitoring on the document level
46092             //this.mon(btn, 'mouseout', this.onMouseOut, this);
46093         }
46094
46095         if(this.menu){
46096             this.mon(this.menu, {
46097                 scope: this,
46098                 show: this.onMenuShow,
46099                 hide: this.onMenuHide
46100             });
46101         }
46102
46103         if(this.repeat){
46104             var repeater = new Ext.util.ClickRepeater(btn, Ext.isObject(this.repeat) ? this.repeat : {});
46105             this.mon(repeater, 'click', this.onClick, this);
46106         }
46107         this.mon(btn, this.clickEvent, this.onClick, this);
46108     },
46109
46110     // private
46111     afterRender : function(){
46112         Ext.Button.superclass.afterRender.call(this);
46113         this.useSetClass = true;
46114         this.setButtonClass();
46115         this.doc = Ext.getDoc();
46116         this.doAutoWidth();
46117     },
46118
46119     /**
46120      * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
46121      * the value of the {@link iconCls} config internally.
46122      * @param {String} cls The CSS class providing the icon image
46123      * @return {Ext.Button} this
46124      */
46125     setIconClass : function(cls){
46126         this.iconCls = cls;
46127         if(this.el){
46128             this.btnEl.dom.className = '';
46129             this.btnEl.addClass(['x-btn-text', cls || '']);
46130             this.setButtonClass();
46131         }
46132         return this;
46133     },
46134
46135     /**
46136      * Sets the tooltip for this Button.
46137      * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
46138      * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
46139      * <li><b>Object</b> : A configuration object for {@link Ext.QuickTips#register}.</li>
46140      * </ul></div>
46141      * @return {Ext.Button} this
46142      */
46143     setTooltip : function(tooltip, /* private */ initial){
46144         if(this.rendered){
46145             if(!initial){
46146                 this.clearTip();
46147             }
46148             if(Ext.isObject(tooltip)){
46149                 Ext.QuickTips.register(Ext.apply({
46150                       target: this.btnEl.id
46151                 }, tooltip));
46152                 this.tooltip = tooltip;
46153             }else{
46154                 this.btnEl.dom[this.tooltipType] = tooltip;
46155             }
46156         }else{
46157             this.tooltip = tooltip;
46158         }
46159         return this;
46160     },
46161
46162     // private
46163     clearTip : function(){
46164         if(Ext.isObject(this.tooltip)){
46165             Ext.QuickTips.unregister(this.btnEl);
46166         }
46167     },
46168
46169     // private
46170     beforeDestroy : function(){
46171         if(this.rendered){
46172             this.clearTip();
46173         }
46174         if(this.menu && this.destroyMenu !== false) {
46175             Ext.destroy(this.menu);
46176         }
46177         Ext.destroy(this.repeater);
46178     },
46179
46180     // private
46181     onDestroy : function(){
46182         if(this.rendered){
46183             this.doc.un('mouseover', this.monitorMouseOver, this);
46184             this.doc.un('mouseup', this.onMouseUp, this);
46185             delete this.doc;
46186             delete this.btnEl;
46187             Ext.ButtonToggleMgr.unregister(this);
46188         }
46189         Ext.Button.superclass.onDestroy.call(this);
46190     },
46191
46192     // private
46193     doAutoWidth : function(){
46194         if(this.autoWidth !== false && this.el && this.text && this.width === undefined){
46195             this.el.setWidth('auto');
46196             if(Ext.isIE7 && Ext.isStrict){
46197                 var ib = this.btnEl;
46198                 if(ib && ib.getWidth() > 20){
46199                     ib.clip();
46200                     ib.setWidth(Ext.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
46201                 }
46202             }
46203             if(this.minWidth){
46204                 if(this.el.getWidth() < this.minWidth){
46205                     this.el.setWidth(this.minWidth);
46206                 }
46207             }
46208         }
46209     },
46210
46211     /**
46212      * Assigns this Button's click handler
46213      * @param {Function} handler The function to call when the button is clicked
46214      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
46215      * Defaults to this Button.
46216      * @return {Ext.Button} this
46217      */
46218     setHandler : function(handler, scope){
46219         this.handler = handler;
46220         this.scope = scope;
46221         return this;
46222     },
46223
46224     /**
46225      * Sets this Button's text
46226      * @param {String} text The button text
46227      * @return {Ext.Button} this
46228      */
46229     setText : function(text){
46230         this.text = text;
46231         if(this.el){
46232             this.btnEl.update(text || '&#160;');
46233             this.setButtonClass();
46234         }
46235         this.doAutoWidth();
46236         return this;
46237     },
46238
46239     /**
46240      * Sets the background image (inline style) of the button.  This method also changes
46241      * the value of the {@link icon} config internally.
46242      * @param {String} icon The path to an image to display in the button
46243      * @return {Ext.Button} this
46244      */
46245     setIcon : function(icon){
46246         this.icon = icon;
46247         if(this.el){
46248             this.btnEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
46249             this.setButtonClass();
46250         }
46251         return this;
46252     },
46253
46254     /**
46255      * Gets the text for this Button
46256      * @return {String} The button text
46257      */
46258     getText : function(){
46259         return this.text;
46260     },
46261
46262     /**
46263      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
46264      * @param {Boolean} state (optional) Force a particular state
46265      * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
46266      * @return {Ext.Button} this
46267      */
46268     toggle : function(state, suppressEvent){
46269         state = state === undefined ? !this.pressed : !!state;
46270         if(state != this.pressed){
46271             if(this.rendered){
46272                 this.el[state ? 'addClass' : 'removeClass']('x-btn-pressed');
46273             }
46274             this.pressed = state;
46275             if(!suppressEvent){
46276                 this.fireEvent('toggle', this, state);
46277                 if(this.toggleHandler){
46278                     this.toggleHandler.call(this.scope || this, this, state);
46279                 }
46280             }
46281         }
46282         return this;
46283     },
46284
46285     // private
46286     onDisable : function(){
46287         this.onDisableChange(true);
46288     },
46289
46290     // private
46291     onEnable : function(){
46292         this.onDisableChange(false);
46293     },
46294
46295     onDisableChange : function(disabled){
46296         if(this.el){
46297             if(!Ext.isIE6 || !this.text){
46298                 this.el[disabled ? 'addClass' : 'removeClass'](this.disabledClass);
46299             }
46300             this.el.dom.disabled = disabled;
46301         }
46302         this.disabled = disabled;
46303     },
46304
46305     /**
46306      * Show this button's menu (if it has one)
46307      */
46308     showMenu : function(){
46309         if(this.rendered && this.menu){
46310             if(this.tooltip){
46311                 Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
46312             }
46313             if(this.menu.isVisible()){
46314                 this.menu.hide();
46315             }
46316             this.menu.ownerCt = this;
46317             this.menu.show(this.el, this.menuAlign);
46318         }
46319         return this;
46320     },
46321
46322     /**
46323      * Hide this button's menu (if it has one)
46324      */
46325     hideMenu : function(){
46326         if(this.hasVisibleMenu()){
46327             this.menu.hide();
46328         }
46329         return this;
46330     },
46331
46332     /**
46333      * Returns true if the button has a menu and it is visible
46334      * @return {Boolean}
46335      */
46336     hasVisibleMenu : function(){
46337         return this.menu && this.menu.ownerCt == this && this.menu.isVisible();
46338     },
46339
46340     // private
46341     onClick : function(e){
46342         if(e){
46343             e.preventDefault();
46344         }
46345         if(e.button !== 0){
46346             return;
46347         }
46348         if(!this.disabled){
46349             if(this.enableToggle && (this.allowDepress !== false || !this.pressed)){
46350                 this.toggle();
46351             }
46352             if(this.menu && !this.hasVisibleMenu() && !this.ignoreNextClick){
46353                 this.showMenu();
46354             }
46355             this.fireEvent('click', this, e);
46356             if(this.handler){
46357                 //this.el.removeClass('x-btn-over');
46358                 this.handler.call(this.scope || this, this, e);
46359             }
46360         }
46361     },
46362
46363     // private
46364     isMenuTriggerOver : function(e, internal){
46365         return this.menu && !internal;
46366     },
46367
46368     // private
46369     isMenuTriggerOut : function(e, internal){
46370         return this.menu && !internal;
46371     },
46372
46373     // private
46374     onMouseOver : function(e){
46375         if(!this.disabled){
46376             var internal = e.within(this.el,  true);
46377             if(!internal){
46378                 this.el.addClass('x-btn-over');
46379                 if(!this.monitoringMouseOver){
46380                     this.doc.on('mouseover', this.monitorMouseOver, this);
46381                     this.monitoringMouseOver = true;
46382                 }
46383                 this.fireEvent('mouseover', this, e);
46384             }
46385             if(this.isMenuTriggerOver(e, internal)){
46386                 this.fireEvent('menutriggerover', this, this.menu, e);
46387             }
46388         }
46389     },
46390
46391     // private
46392     monitorMouseOver : function(e){
46393         if(e.target != this.el.dom && !e.within(this.el)){
46394             if(this.monitoringMouseOver){
46395                 this.doc.un('mouseover', this.monitorMouseOver, this);
46396                 this.monitoringMouseOver = false;
46397             }
46398             this.onMouseOut(e);
46399         }
46400     },
46401
46402     // private
46403     onMouseOut : function(e){
46404         var internal = e.within(this.el) && e.target != this.el.dom;
46405         this.el.removeClass('x-btn-over');
46406         this.fireEvent('mouseout', this, e);
46407         if(this.isMenuTriggerOut(e, internal)){
46408             this.fireEvent('menutriggerout', this, this.menu, e);
46409         }
46410     },
46411
46412     focus : function() {
46413         this.btnEl.focus();
46414     },
46415
46416     blur : function() {
46417         this.btnEl.blur();
46418     },
46419
46420     // private
46421     onFocus : function(e){
46422         if(!this.disabled){
46423             this.el.addClass('x-btn-focus');
46424         }
46425     },
46426     // private
46427     onBlur : function(e){
46428         this.el.removeClass('x-btn-focus');
46429     },
46430
46431     // private
46432     getClickEl : function(e, isUp){
46433        return this.el;
46434     },
46435
46436     // private
46437     onMouseDown : function(e){
46438         if(!this.disabled && e.button === 0){
46439             this.getClickEl(e).addClass('x-btn-click');
46440             this.doc.on('mouseup', this.onMouseUp, this);
46441         }
46442     },
46443     // private
46444     onMouseUp : function(e){
46445         if(e.button === 0){
46446             this.getClickEl(e, true).removeClass('x-btn-click');
46447             this.doc.un('mouseup', this.onMouseUp, this);
46448         }
46449     },
46450     // private
46451     onMenuShow : function(e){
46452         if(this.menu.ownerCt == this){
46453             this.menu.ownerCt = this;
46454             this.ignoreNextClick = 0;
46455             this.el.addClass('x-btn-menu-active');
46456             this.fireEvent('menushow', this, this.menu);
46457         }
46458     },
46459     // private
46460     onMenuHide : function(e){
46461         if(this.menu.ownerCt == this){
46462             this.el.removeClass('x-btn-menu-active');
46463             this.ignoreNextClick = this.restoreClick.defer(250, this);
46464             this.fireEvent('menuhide', this, this.menu);
46465             delete this.menu.ownerCt;
46466         }
46467     },
46468
46469     // private
46470     restoreClick : function(){
46471         this.ignoreNextClick = 0;
46472     }
46473
46474     /**
46475      * @cfg {String} autoEl @hide
46476      */
46477     /**
46478      * @cfg {String/Object} html @hide
46479      */
46480     /**
46481      * @cfg {String} contentEl  @hide
46482      */
46483     /**
46484      * @cfg {Mixed} data  @hide
46485      */
46486     /**
46487      * @cfg {Mixed} tpl  @hide
46488      */
46489     /**
46490      * @cfg {String} tplWriteMode  @hide
46491      */
46492 });
46493 Ext.reg('button', Ext.Button);
46494
46495 // Private utility class used by Button
46496 Ext.ButtonToggleMgr = function(){
46497    var groups = {};
46498
46499    function toggleGroup(btn, state){
46500        if(state){
46501            var g = groups[btn.toggleGroup];
46502            for(var i = 0, l = g.length; i < l; i++){
46503                if(g[i] != btn){
46504                    g[i].toggle(false);
46505                }
46506            }
46507        }
46508    }
46509
46510    return {
46511        register : function(btn){
46512            if(!btn.toggleGroup){
46513                return;
46514            }
46515            var g = groups[btn.toggleGroup];
46516            if(!g){
46517                g = groups[btn.toggleGroup] = [];
46518            }
46519            g.push(btn);
46520            btn.on('toggle', toggleGroup);
46521        },
46522
46523        unregister : function(btn){
46524            if(!btn.toggleGroup){
46525                return;
46526            }
46527            var g = groups[btn.toggleGroup];
46528            if(g){
46529                g.remove(btn);
46530                btn.un('toggle', toggleGroup);
46531            }
46532        },
46533
46534        /**
46535         * Gets the pressed button in the passed group or null
46536         * @param {String} group
46537         * @return Button
46538         */
46539        getPressed : function(group){
46540            var g = groups[group];
46541            if(g){
46542                for(var i = 0, len = g.length; i < len; i++){
46543                    if(g[i].pressed === true){
46544                        return g[i];
46545                    }
46546                }
46547            }
46548            return null;
46549        }
46550    };
46551 }();
46552 /**
46553  * @class Ext.SplitButton
46554  * @extends Ext.Button
46555  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
46556  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
46557  * options to the primary button action, but any custom handler can provide the arrowclick implementation.  Example usage:
46558  * <pre><code>
46559 // display a dropdown menu:
46560 new Ext.SplitButton({
46561         renderTo: 'button-ct', // the container id
46562         text: 'Options',
46563         handler: optionsHandler, // handle a click on the button itself
46564         menu: new Ext.menu.Menu({
46565         items: [
46566                 // these items will render as dropdown menu items when the arrow is clicked:
46567                 {text: 'Item 1', handler: item1Handler},
46568                 {text: 'Item 2', handler: item2Handler}
46569         ]
46570         })
46571 });
46572
46573 // Instead of showing a menu, you provide any type of custom
46574 // functionality you want when the dropdown arrow is clicked:
46575 new Ext.SplitButton({
46576         renderTo: 'button-ct',
46577         text: 'Options',
46578         handler: optionsHandler,
46579         arrowHandler: myCustomHandler
46580 });
46581 </code></pre>
46582  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
46583  * @cfg {String} arrowTooltip The title attribute of the arrow
46584  * @constructor
46585  * Create a new menu button
46586  * @param {Object} config The config object
46587  * @xtype splitbutton
46588  */
46589 Ext.SplitButton = Ext.extend(Ext.Button, {
46590         // private
46591     arrowSelector : 'em',
46592     split: true,
46593
46594     // private
46595     initComponent : function(){
46596         Ext.SplitButton.superclass.initComponent.call(this);
46597         /**
46598          * @event arrowclick
46599          * Fires when this button's arrow is clicked
46600          * @param {MenuButton} this
46601          * @param {EventObject} e The click event
46602          */
46603         this.addEvents("arrowclick");
46604     },
46605
46606     // private
46607     onRender : function(){
46608         Ext.SplitButton.superclass.onRender.apply(this, arguments);
46609         if(this.arrowTooltip){
46610             this.el.child(this.arrowSelector).dom[this.tooltipType] = this.arrowTooltip;
46611         }
46612     },
46613
46614     /**
46615      * Sets this button's arrow click handler.
46616      * @param {Function} handler The function to call when the arrow is clicked
46617      * @param {Object} scope (optional) Scope for the function passed above
46618      */
46619     setArrowHandler : function(handler, scope){
46620         this.arrowHandler = handler;
46621         this.scope = scope;
46622     },
46623
46624     getMenuClass : function(){
46625         return 'x-btn-split' + (this.arrowAlign == 'bottom' ? '-bottom' : '');
46626     },
46627
46628     isClickOnArrow : function(e){
46629         if (this.arrowAlign != 'bottom') {
46630             var visBtn = this.el.child('em.x-btn-split');
46631             var right = visBtn.getRegion().right - visBtn.getPadding('r');
46632             return e.getPageX() > right;
46633         } else {
46634             return e.getPageY() > this.btnEl.getRegion().bottom;
46635         }
46636     },
46637
46638     // private
46639     onClick : function(e, t){
46640         e.preventDefault();
46641         if(!this.disabled){
46642             if(this.isClickOnArrow(e)){
46643                 if(this.menu && !this.menu.isVisible() && !this.ignoreNextClick){
46644                     this.showMenu();
46645                 }
46646                 this.fireEvent("arrowclick", this, e);
46647                 if(this.arrowHandler){
46648                     this.arrowHandler.call(this.scope || this, this, e);
46649                 }
46650             }else{
46651                 if(this.enableToggle){
46652                     this.toggle();
46653                 }
46654                 this.fireEvent("click", this, e);
46655                 if(this.handler){
46656                     this.handler.call(this.scope || this, this, e);
46657                 }
46658             }
46659         }
46660     },
46661
46662     // private
46663     isMenuTriggerOver : function(e){
46664         return this.menu && e.target.tagName == this.arrowSelector;
46665     },
46666
46667     // private
46668     isMenuTriggerOut : function(e, internal){
46669         return this.menu && e.target.tagName != this.arrowSelector;
46670     }
46671 });
46672
46673 Ext.reg('splitbutton', Ext.SplitButton);/**
46674  * @class Ext.CycleButton
46675  * @extends Ext.SplitButton
46676  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically
46677  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's
46678  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the
46679  * button displays the dropdown menu just like a normal SplitButton.  Example usage:
46680  * <pre><code>
46681 var btn = new Ext.CycleButton({
46682     showText: true,
46683     prependText: 'View as ',
46684     items: [{
46685         text:'text only',
46686         iconCls:'view-text',
46687         checked:true
46688     },{
46689         text:'HTML',
46690         iconCls:'view-html'
46691     }],
46692     changeHandler:function(btn, item){
46693         Ext.Msg.alert('Change View', item.text);
46694     }
46695 });
46696 </code></pre>
46697  * @constructor
46698  * Create a new split button
46699  * @param {Object} config The config object
46700  * @xtype cycle
46701  */
46702 Ext.CycleButton = Ext.extend(Ext.SplitButton, {
46703     /**
46704      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the
46705      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})
46706      */
46707     /**
46708      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)
46709      */
46710     /**
46711      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the
46712      * button's text (only applies when showText = true, defaults to '')
46713      */
46714     /**
46715      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu
46716      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead
46717      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the
46718      * following argument list: (SplitButton this, Ext.menu.CheckItem item)
46719      */
46720     /**
46721      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This
46722      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the 
46723      * default behavior of changing the button's icon to match the selected item's icon on change.
46724      */
46725     /**
46726      * @property menu
46727      * @type Menu
46728      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.
46729      */
46730
46731     // private
46732     getItemText : function(item){
46733         if(item && this.showText === true){
46734             var text = '';
46735             if(this.prependText){
46736                 text += this.prependText;
46737             }
46738             text += item.text;
46739             return text;
46740         }
46741         return undefined;
46742     },
46743
46744     /**
46745      * Sets the button's active menu item.
46746      * @param {Ext.menu.CheckItem} item The item to activate
46747      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)
46748      */
46749     setActiveItem : function(item, suppressEvent){
46750         if(!Ext.isObject(item)){
46751             item = this.menu.getComponent(item);
46752         }
46753         if(item){
46754             if(!this.rendered){
46755                 this.text = this.getItemText(item);
46756                 this.iconCls = item.iconCls;
46757             }else{
46758                 var t = this.getItemText(item);
46759                 if(t){
46760                     this.setText(t);
46761                 }
46762                 this.setIconClass(item.iconCls);
46763             }
46764             this.activeItem = item;
46765             if(!item.checked){
46766                 item.setChecked(true, false);
46767             }
46768             if(this.forceIcon){
46769                 this.setIconClass(this.forceIcon);
46770             }
46771             if(!suppressEvent){
46772                 this.fireEvent('change', this, item);
46773             }
46774         }
46775     },
46776
46777     /**
46778      * Gets the currently active menu item.
46779      * @return {Ext.menu.CheckItem} The active item
46780      */
46781     getActiveItem : function(){
46782         return this.activeItem;
46783     },
46784
46785     // private
46786     initComponent : function(){
46787         this.addEvents(
46788             /**
46789              * @event change
46790              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function
46791              * is set on this CycleButton, it will be called instead on active item change and this change event will
46792              * not be fired.
46793              * @param {Ext.CycleButton} this
46794              * @param {Ext.menu.CheckItem} item The menu item that was selected
46795              */
46796             "change"
46797         );
46798
46799         if(this.changeHandler){
46800             this.on('change', this.changeHandler, this.scope||this);
46801             delete this.changeHandler;
46802         }
46803
46804         this.itemCount = this.items.length;
46805
46806         this.menu = {cls:'x-cycle-menu', items:[]};
46807         var checked = 0;
46808         Ext.each(this.items, function(item, i){
46809             Ext.apply(item, {
46810                 group: item.group || this.id,
46811                 itemIndex: i,
46812                 checkHandler: this.checkHandler,
46813                 scope: this,
46814                 checked: item.checked || false
46815             });
46816             this.menu.items.push(item);
46817             if(item.checked){
46818                 checked = i;
46819             }
46820         }, this);
46821         Ext.CycleButton.superclass.initComponent.call(this);
46822         this.on('click', this.toggleSelected, this);
46823         this.setActiveItem(checked, true);
46824     },
46825
46826     // private
46827     checkHandler : function(item, pressed){
46828         if(pressed){
46829             this.setActiveItem(item);
46830         }
46831     },
46832
46833     /**
46834      * This is normally called internally on button click, but can be called externally to advance the button's
46835      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu
46836      * the active item will be set to the first item in the menu.
46837      */
46838     toggleSelected : function(){
46839         var m = this.menu;
46840         m.render();
46841         // layout if we haven't before so the items are active
46842         if(!m.hasLayout){
46843             m.doLayout();
46844         }
46845         
46846         var nextIdx, checkItem;
46847         for (var i = 1; i < this.itemCount; i++) {
46848             nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;
46849             // check the potential item
46850             checkItem = m.items.itemAt(nextIdx);
46851             // if its not disabled then check it.
46852             if (!checkItem.disabled) {
46853                 checkItem.setChecked(true);
46854                 break;
46855             }
46856         }
46857     }
46858 });
46859 Ext.reg('cycle', Ext.CycleButton);/**
46860  * @class Ext.Toolbar
46861  * @extends Ext.Container
46862  * <p>Basic Toolbar class. Although the <tt>{@link Ext.Container#defaultType defaultType}</tt> for Toolbar
46863  * is <tt>{@link Ext.Button button}</tt>, Toolbar elements (child items for the Toolbar container) may
46864  * be virtually any type of Component. Toolbar elements can be created explicitly via their constructors,
46865  * or implicitly via their xtypes, and can be <tt>{@link #add}</tt>ed dynamically.</p>
46866  * <p>Some items have shortcut strings for creation:</p>
46867  * <pre>
46868 <u>Shortcut</u>  <u>xtype</u>          <u>Class</u>                  <u>Description</u>
46869 '->'      'tbfill'       {@link Ext.Toolbar.Fill}       begin using the right-justified button container
46870 '-'       'tbseparator'  {@link Ext.Toolbar.Separator}  add a vertical separator bar between toolbar items
46871 ' '       'tbspacer'     {@link Ext.Toolbar.Spacer}     add horiztonal space between elements
46872  * </pre>
46873  *
46874  * Example usage of various elements:
46875  * <pre><code>
46876 var tb = new Ext.Toolbar({
46877     renderTo: document.body,
46878     width: 600,
46879     height: 100,
46880     items: [
46881         {
46882             // xtype: 'button', // default for Toolbars, same as 'tbbutton'
46883             text: 'Button'
46884         },
46885         {
46886             xtype: 'splitbutton', // same as 'tbsplitbutton'
46887             text: 'Split Button'
46888         },
46889         // begin using the right-justified button container
46890         '->', // same as {xtype: 'tbfill'}, // Ext.Toolbar.Fill
46891         {
46892             xtype: 'textfield',
46893             name: 'field1',
46894             emptyText: 'enter search term'
46895         },
46896         // add a vertical separator bar between toolbar items
46897         '-', // same as {xtype: 'tbseparator'} to create Ext.Toolbar.Separator
46898         'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.Toolbar.TextItem
46899         {xtype: 'tbspacer'},// same as ' ' to create Ext.Toolbar.Spacer
46900         'text 2',
46901         {xtype: 'tbspacer', width: 50}, // add a 50px space
46902         'text 3'
46903     ]
46904 });
46905  * </code></pre>
46906  * Example adding a ComboBox within a menu of a button:
46907  * <pre><code>
46908 // ComboBox creation
46909 var combo = new Ext.form.ComboBox({
46910     store: new Ext.data.ArrayStore({
46911         autoDestroy: true,
46912         fields: ['initials', 'fullname'],
46913         data : [
46914             ['FF', 'Fred Flintstone'],
46915             ['BR', 'Barney Rubble']
46916         ]
46917     }),
46918     displayField: 'fullname',
46919     typeAhead: true,
46920     mode: 'local',
46921     forceSelection: true,
46922     triggerAction: 'all',
46923     emptyText: 'Select a name...',
46924     selectOnFocus: true,
46925     width: 135,
46926     getListParent: function() {
46927         return this.el.up('.x-menu');
46928     },
46929     iconCls: 'no-icon' //use iconCls if placing within menu to shift to right side of menu
46930 });
46931
46932 // put ComboBox in a Menu
46933 var menu = new Ext.menu.Menu({
46934     id: 'mainMenu',
46935     items: [
46936         combo // A Field in a Menu
46937     ]
46938 });
46939
46940 // add a Button with the menu
46941 tb.add({
46942         text:'Button w/ Menu',
46943         menu: menu  // assign menu by instance
46944     });
46945 tb.doLayout();
46946  * </code></pre>
46947  * @constructor
46948  * Creates a new Toolbar
46949  * @param {Object/Array} config A config object or an array of buttons to <tt>{@link #add}</tt>
46950  * @xtype toolbar
46951  */
46952 Ext.Toolbar = function(config){
46953     if(Ext.isArray(config)){
46954         config = {items: config, layout: 'toolbar'};
46955     } else {
46956         config = Ext.apply({
46957             layout: 'toolbar'
46958         }, config);
46959         if(config.buttons) {
46960             config.items = config.buttons;
46961         }
46962     }
46963     Ext.Toolbar.superclass.constructor.call(this, config);
46964 };
46965
46966 (function(){
46967
46968 var T = Ext.Toolbar;
46969
46970 Ext.extend(T, Ext.Container, {
46971
46972     defaultType: 'button',
46973
46974     /**
46975      * @cfg {String/Object} layout
46976      * This class assigns a default layout (<code>layout:'<b>toolbar</b>'</code>).
46977      * Developers <i>may</i> override this configuration option if another layout
46978      * is required (the constructor must be passed a configuration object in this
46979      * case instead of an array).
46980      * See {@link Ext.Container#layout} for additional information.
46981      */
46982
46983     enableOverflow : false,
46984
46985     /**
46986      * @cfg {Boolean} enableOverflow
46987      * Defaults to false. Configure <code>true<code> to make the toolbar provide a button
46988      * which activates a dropdown Menu to show items which overflow the Toolbar's width.
46989      */
46990     /**
46991      * @cfg {String} buttonAlign
46992      * <p>The default position at which to align child items. Defaults to <code>"left"</code></p>
46993      * <p>May be specified as <code>"center"</code> to cause items added before a Fill (A <code>"->"</code>) item
46994      * to be centered in the Toolbar. Items added after a Fill are still right-aligned.</p>
46995      * <p>Specify as <code>"right"</code> to right align all child items.</p>
46996      */
46997
46998     trackMenus : true,
46999     internalDefaults: {removeMode: 'container', hideParent: true},
47000     toolbarCls: 'x-toolbar',
47001
47002     initComponent : function(){
47003         T.superclass.initComponent.call(this);
47004
47005         /**
47006          * @event overflowchange
47007          * Fires after the overflow state has changed.
47008          * @param {Object} c The Container
47009          * @param {Boolean} lastOverflow overflow state
47010          */
47011         this.addEvents('overflowchange');
47012     },
47013
47014     // private
47015     onRender : function(ct, position){
47016         if(!this.el){
47017             if(!this.autoCreate){
47018                 this.autoCreate = {
47019                     cls: this.toolbarCls + ' x-small-editor'
47020                 };
47021             }
47022             this.el = ct.createChild(Ext.apply({ id: this.id },this.autoCreate), position);
47023             Ext.Toolbar.superclass.onRender.apply(this, arguments);
47024         }
47025     },
47026
47027     /**
47028      * <p>Adds element(s) to the toolbar -- this function takes a variable number of
47029      * arguments of mixed type and adds them to the toolbar.</p>
47030      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47031      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
47032      * <ul>
47033      * <li>{@link Ext.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
47034      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
47035      * <li>Field: Any form field (equivalent to {@link #addField})</li>
47036      * <li>Item: Any subclass of {@link Ext.Toolbar.Item} (equivalent to {@link #addItem})</li>
47037      * <li>String: Any generic string (gets wrapped in a {@link Ext.Toolbar.TextItem}, equivalent to {@link #addText}).
47038      * Note that there are a few special strings that are treated differently as explained next.</li>
47039      * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>
47040      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
47041      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
47042      * </ul>
47043      * @param {Mixed} arg2
47044      * @param {Mixed} etc.
47045      * @method add
47046      */
47047
47048     // private
47049     lookupComponent : function(c){
47050         if(Ext.isString(c)){
47051             if(c == '-'){
47052                 c = new T.Separator();
47053             }else if(c == ' '){
47054                 c = new T.Spacer();
47055             }else if(c == '->'){
47056                 c = new T.Fill();
47057             }else{
47058                 c = new T.TextItem(c);
47059             }
47060             this.applyDefaults(c);
47061         }else{
47062             if(c.isFormField || c.render){ // some kind of form field, some kind of Toolbar.Item
47063                 c = this.createComponent(c);
47064             }else if(c.tag){ // DomHelper spec
47065                 c = new T.Item({autoEl: c});
47066             }else if(c.tagName){ // element
47067                 c = new T.Item({el:c});
47068             }else if(Ext.isObject(c)){ // must be button config?
47069                 c = c.xtype ? this.createComponent(c) : this.constructButton(c);
47070             }
47071         }
47072         return c;
47073     },
47074
47075     // private
47076     applyDefaults : function(c){
47077         if(!Ext.isString(c)){
47078             c = Ext.Toolbar.superclass.applyDefaults.call(this, c);
47079             var d = this.internalDefaults;
47080             if(c.events){
47081                 Ext.applyIf(c.initialConfig, d);
47082                 Ext.apply(c, d);
47083             }else{
47084                 Ext.applyIf(c, d);
47085             }
47086         }
47087         return c;
47088     },
47089
47090     /**
47091      * Adds a separator
47092      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47093      * @return {Ext.Toolbar.Item} The separator {@link Ext.Toolbar.Item item}
47094      */
47095     addSeparator : function(){
47096         return this.add(new T.Separator());
47097     },
47098
47099     /**
47100      * Adds a spacer element
47101      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47102      * @return {Ext.Toolbar.Spacer} The spacer item
47103      */
47104     addSpacer : function(){
47105         return this.add(new T.Spacer());
47106     },
47107
47108     /**
47109      * Forces subsequent additions into the float:right toolbar
47110      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47111      */
47112     addFill : function(){
47113         this.add(new T.Fill());
47114     },
47115
47116     /**
47117      * Adds any standard HTML element to the toolbar
47118      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47119      * @param {Mixed} el The element or id of the element to add
47120      * @return {Ext.Toolbar.Item} The element's item
47121      */
47122     addElement : function(el){
47123         return this.addItem(new T.Item({el:el}));
47124     },
47125
47126     /**
47127      * Adds any Toolbar.Item or subclass
47128      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47129      * @param {Ext.Toolbar.Item} item
47130      * @return {Ext.Toolbar.Item} The item
47131      */
47132     addItem : function(item){
47133         return this.add.apply(this, arguments);
47134     },
47135
47136     /**
47137      * Adds a button (or buttons). See {@link Ext.Button} for more info on the config.
47138      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47139      * @param {Object/Array} config A button config or array of configs
47140      * @return {Ext.Button/Array}
47141      */
47142     addButton : function(config){
47143         if(Ext.isArray(config)){
47144             var buttons = [];
47145             for(var i = 0, len = config.length; i < len; i++) {
47146                 buttons.push(this.addButton(config[i]));
47147             }
47148             return buttons;
47149         }
47150         return this.add(this.constructButton(config));
47151     },
47152
47153     /**
47154      * Adds text to the toolbar
47155      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47156      * @param {String} text The text to add
47157      * @return {Ext.Toolbar.Item} The element's item
47158      */
47159     addText : function(text){
47160         return this.addItem(new T.TextItem(text));
47161     },
47162
47163     /**
47164      * Adds a new element to the toolbar from the passed {@link Ext.DomHelper} config
47165      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47166      * @param {Object} config
47167      * @return {Ext.Toolbar.Item} The element's item
47168      */
47169     addDom : function(config){
47170         return this.add(new T.Item({autoEl: config}));
47171     },
47172
47173     /**
47174      * Adds a dynamically rendered Ext.form field (TextField, ComboBox, etc). Note: the field should not have
47175      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
47176      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47177      * @param {Ext.form.Field} field
47178      * @return {Ext.Toolbar.Item}
47179      */
47180     addField : function(field){
47181         return this.add(field);
47182     },
47183
47184     /**
47185      * Inserts any {@link Ext.Toolbar.Item}/{@link Ext.Button} at the specified index.
47186      * <br><p><b>Note</b>: See the notes within {@link Ext.Container#add}.</p>
47187      * @param {Number} index The index where the item is to be inserted
47188      * @param {Object/Ext.Toolbar.Item/Ext.Button/Array} item The button, or button config object to be
47189      * inserted, or an array of buttons/configs.
47190      * @return {Ext.Button/Item}
47191      */
47192     insertButton : function(index, item){
47193         if(Ext.isArray(item)){
47194             var buttons = [];
47195             for(var i = 0, len = item.length; i < len; i++) {
47196                buttons.push(this.insertButton(index + i, item[i]));
47197             }
47198             return buttons;
47199         }
47200         return Ext.Toolbar.superclass.insert.call(this, index, item);
47201     },
47202
47203     // private
47204     trackMenu : function(item, remove){
47205         if(this.trackMenus && item.menu){
47206             var method = remove ? 'mun' : 'mon';
47207             this[method](item, 'menutriggerover', this.onButtonTriggerOver, this);
47208             this[method](item, 'menushow', this.onButtonMenuShow, this);
47209             this[method](item, 'menuhide', this.onButtonMenuHide, this);
47210         }
47211     },
47212
47213     // private
47214     constructButton : function(item){
47215         var b = item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
47216         return b;
47217     },
47218
47219     // private
47220     onAdd : function(c){
47221         Ext.Toolbar.superclass.onAdd.call(this);
47222         this.trackMenu(c);
47223         if(this.disabled){
47224             c.disable();
47225         }
47226     },
47227
47228     // private
47229     onRemove : function(c){
47230         Ext.Toolbar.superclass.onRemove.call(this);
47231         this.trackMenu(c, true);
47232     },
47233
47234     // private
47235     onDisable : function(){
47236         this.items.each(function(item){
47237              if(item.disable){
47238                  item.disable();
47239              }
47240         });
47241     },
47242
47243     // private
47244     onEnable : function(){
47245         this.items.each(function(item){
47246              if(item.enable){
47247                  item.enable();
47248              }
47249         });
47250     },
47251
47252     // private
47253     onButtonTriggerOver : function(btn){
47254         if(this.activeMenuBtn && this.activeMenuBtn != btn){
47255             this.activeMenuBtn.hideMenu();
47256             btn.showMenu();
47257             this.activeMenuBtn = btn;
47258         }
47259     },
47260
47261     // private
47262     onButtonMenuShow : function(btn){
47263         this.activeMenuBtn = btn;
47264     },
47265
47266     // private
47267     onButtonMenuHide : function(btn){
47268         delete this.activeMenuBtn;
47269     }
47270 });
47271 Ext.reg('toolbar', Ext.Toolbar);
47272
47273 /**
47274  * @class Ext.Toolbar.Item
47275  * @extends Ext.BoxComponent
47276  * The base class that other non-interacting Toolbar Item classes should extend in order to
47277  * get some basic common toolbar item functionality.
47278  * @constructor
47279  * Creates a new Item
47280  * @param {HTMLElement} el
47281  * @xtype tbitem
47282  */
47283 T.Item = Ext.extend(Ext.BoxComponent, {
47284     hideParent: true, //  Hiding a Toolbar.Item hides its containing TD
47285     enable:Ext.emptyFn,
47286     disable:Ext.emptyFn,
47287     focus:Ext.emptyFn
47288     /**
47289      * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.
47290      */
47291 });
47292 Ext.reg('tbitem', T.Item);
47293
47294 /**
47295  * @class Ext.Toolbar.Separator
47296  * @extends Ext.Toolbar.Item
47297  * A simple class that adds a vertical separator bar between toolbar items
47298  * (css class:<tt>'xtb-sep'</tt>). Example usage:
47299  * <pre><code>
47300 new Ext.Panel({
47301     tbar : [
47302         'Item 1',
47303         {xtype: 'tbseparator'}, // or '-'
47304         'Item 2'
47305     ]
47306 });
47307 </code></pre>
47308  * @constructor
47309  * Creates a new Separator
47310  * @xtype tbseparator
47311  */
47312 T.Separator = Ext.extend(T.Item, {
47313     onRender : function(ct, position){
47314         this.el = ct.createChild({tag:'span', cls:'xtb-sep'}, position);
47315     }
47316 });
47317 Ext.reg('tbseparator', T.Separator);
47318
47319 /**
47320  * @class Ext.Toolbar.Spacer
47321  * @extends Ext.Toolbar.Item
47322  * A simple element that adds extra horizontal space between items in a toolbar.
47323  * By default a 2px wide space is added via css specification:<pre><code>
47324 .x-toolbar .xtb-spacer {
47325     width:2px;
47326 }
47327  * </code></pre>
47328  * <p>Example usage:</p>
47329  * <pre><code>
47330 new Ext.Panel({
47331     tbar : [
47332         'Item 1',
47333         {xtype: 'tbspacer'}, // or ' '
47334         'Item 2',
47335         // space width is also configurable via javascript
47336         {xtype: 'tbspacer', width: 50}, // add a 50px space
47337         'Item 3'
47338     ]
47339 });
47340 </code></pre>
47341  * @constructor
47342  * Creates a new Spacer
47343  * @xtype tbspacer
47344  */
47345 T.Spacer = Ext.extend(T.Item, {
47346     /**
47347      * @cfg {Number} width
47348      * The width of the spacer in pixels (defaults to 2px via css style <tt>.x-toolbar .xtb-spacer</tt>).
47349      */
47350
47351     onRender : function(ct, position){
47352         this.el = ct.createChild({tag:'div', cls:'xtb-spacer', style: this.width?'width:'+this.width+'px':''}, position);
47353     }
47354 });
47355 Ext.reg('tbspacer', T.Spacer);
47356
47357 /**
47358  * @class Ext.Toolbar.Fill
47359  * @extends Ext.Toolbar.Spacer
47360  * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using
47361  * the right-justified button container.
47362  * <pre><code>
47363 new Ext.Panel({
47364     tbar : [
47365         'Item 1',
47366         {xtype: 'tbfill'}, // or '->'
47367         'Item 2'
47368     ]
47369 });
47370 </code></pre>
47371  * @constructor
47372  * Creates a new Fill
47373  * @xtype tbfill
47374  */
47375 T.Fill = Ext.extend(T.Item, {
47376     // private
47377     render : Ext.emptyFn,
47378     isFill : true
47379 });
47380 Ext.reg('tbfill', T.Fill);
47381
47382 /**
47383  * @class Ext.Toolbar.TextItem
47384  * @extends Ext.Toolbar.Item
47385  * A simple class that renders text directly into a toolbar
47386  * (with css class:<tt>'xtb-text'</tt>). Example usage:
47387  * <pre><code>
47388 new Ext.Panel({
47389     tbar : [
47390         {xtype: 'tbtext', text: 'Item 1'} // or simply 'Item 1'
47391     ]
47392 });
47393 </code></pre>
47394  * @constructor
47395  * Creates a new TextItem
47396  * @param {String/Object} text A text string, or a config object containing a <tt>text</tt> property
47397  * @xtype tbtext
47398  */
47399 T.TextItem = Ext.extend(T.Item, {
47400     /**
47401      * @cfg {String} text The text to be used as innerHTML (html tags are accepted)
47402      */
47403
47404     constructor: function(config){
47405         T.TextItem.superclass.constructor.call(this, Ext.isString(config) ? {text: config} : config);
47406     },
47407
47408     // private
47409     onRender : function(ct, position) {
47410         this.autoEl = {cls: 'xtb-text', html: this.text || ''};
47411         T.TextItem.superclass.onRender.call(this, ct, position);
47412     },
47413
47414     /**
47415      * Updates this item's text, setting the text to be used as innerHTML.
47416      * @param {String} t The text to display (html accepted).
47417      */
47418     setText : function(t) {
47419         if(this.rendered){
47420             this.el.update(t);
47421         }else{
47422             this.text = t;
47423         }
47424     }
47425 });
47426 Ext.reg('tbtext', T.TextItem);
47427
47428 // backwards compat
47429 T.Button = Ext.extend(Ext.Button, {});
47430 T.SplitButton = Ext.extend(Ext.SplitButton, {});
47431 Ext.reg('tbbutton', T.Button);
47432 Ext.reg('tbsplit', T.SplitButton);
47433
47434 })();
47435 /**
47436  * @class Ext.ButtonGroup
47437  * @extends Ext.Panel
47438  * Container for a group of buttons. Example usage:
47439  * <pre><code>
47440 var p = new Ext.Panel({
47441     title: 'Panel with Button Group',
47442     width: 300,
47443     height:200,
47444     renderTo: document.body,
47445     html: 'whatever',
47446     tbar: [{
47447         xtype: 'buttongroup',
47448         {@link #columns}: 3,
47449         title: 'Clipboard',
47450         items: [{
47451             text: 'Paste',
47452             scale: 'large',
47453             rowspan: 3, iconCls: 'add',
47454             iconAlign: 'top',
47455             cls: 'x-btn-as-arrow'
47456         },{
47457             xtype:'splitbutton',
47458             text: 'Menu Button',
47459             scale: 'large',
47460             rowspan: 3,
47461             iconCls: 'add',
47462             iconAlign: 'top',
47463             arrowAlign:'bottom',
47464             menu: [{text: 'Menu Item 1'}]
47465         },{
47466             xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]
47467         },{
47468             text: 'Copy', iconCls: 'add16'
47469         },{
47470             text: 'Format', iconCls: 'add16'
47471         }]
47472     }]
47473 });
47474  * </code></pre>
47475  * @constructor
47476  * Create a new ButtonGroup.
47477  * @param {Object} config The config object
47478  * @xtype buttongroup
47479  */
47480 Ext.ButtonGroup = Ext.extend(Ext.Panel, {
47481     /**
47482      * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the
47483      * {@link #layout configured layout manager}. See {@link Ext.layout.TableLayout#columns}.
47484      */
47485     /**
47486      * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.Panel#baseCls}.
47487      */
47488     baseCls: 'x-btn-group',
47489     /**
47490      * @cfg {String} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.Container#layout}.
47491      */
47492     layout:'table',
47493     defaultType: 'button',
47494     /**
47495      * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.Panel#frame}.
47496      */
47497     frame: true,
47498     internalDefaults: {removeMode: 'container', hideParent: true},
47499
47500     initComponent : function(){
47501         this.layoutConfig = this.layoutConfig || {};
47502         Ext.applyIf(this.layoutConfig, {
47503             columns : this.columns
47504         });
47505         if(!this.title){
47506             this.addClass('x-btn-group-notitle');
47507         }
47508         this.on('afterlayout', this.onAfterLayout, this);
47509         Ext.ButtonGroup.superclass.initComponent.call(this);
47510     },
47511
47512     applyDefaults : function(c){
47513         c = Ext.ButtonGroup.superclass.applyDefaults.call(this, c);
47514         var d = this.internalDefaults;
47515         if(c.events){
47516             Ext.applyIf(c.initialConfig, d);
47517             Ext.apply(c, d);
47518         }else{
47519             Ext.applyIf(c, d);
47520         }
47521         return c;
47522     },
47523
47524     onAfterLayout : function(){
47525         var bodyWidth = this.body.getFrameWidth('lr') + this.body.dom.firstChild.offsetWidth;
47526         this.body.setWidth(bodyWidth);
47527         this.el.setWidth(bodyWidth + this.getFrameWidth());
47528     }
47529     /**
47530      * @cfg {Array} tools  @hide
47531      */
47532 });
47533
47534 Ext.reg('buttongroup', Ext.ButtonGroup);
47535 /**
47536  * @class Ext.PagingToolbar
47537  * @extends Ext.Toolbar
47538  * <p>As the amount of records increases, the time required for the browser to render
47539  * them increases. Paging is used to reduce the amount of data exchanged with the client.
47540  * Note: if there are more records/rows than can be viewed in the available screen area, vertical
47541  * scrollbars will be added.</p>
47542  * <p>Paging is typically handled on the server side (see exception below). The client sends
47543  * parameters to the server side, which the server needs to interpret and then respond with the
47544  * approprate data.</p>
47545  * <p><b>Ext.PagingToolbar</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
47546  * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
47547  * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
47548  * paging criteria.</p>
47549  * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
47550  * <pre><code>
47551 Ext.QuickTips.init(); // to display button quicktips
47552
47553 var myStore = new Ext.data.Store({
47554     reader: new Ext.data.JsonReader({
47555         {@link Ext.data.JsonReader#totalProperty totalProperty}: 'results', 
47556         ...
47557     }),
47558     ...
47559 });
47560
47561 var myPageSize = 25;  // server script should only send back 25 items at a time
47562
47563 var grid = new Ext.grid.GridPanel({
47564     ...
47565     store: myStore,
47566     bbar: new Ext.PagingToolbar({
47567         {@link #store}: myStore,       // grid and PagingToolbar using same store
47568         {@link #displayInfo}: true,
47569         {@link #pageSize}: myPageSize,
47570         {@link #prependButtons}: true,
47571         items: [
47572             'text 1'
47573         ]
47574     })
47575 });
47576  * </code></pre>
47577  *
47578  * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
47579  * <pre><code>
47580 store.load({
47581     params: {
47582         // specify params for the first page load if using paging
47583         start: 0,          
47584         limit: myPageSize,
47585         // other params
47586         foo:   'bar'
47587     }
47588 });
47589  * </code></pre>
47590  * 
47591  * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
47592  * <pre><code>
47593 var myStore = new Ext.data.Store({
47594     {@link Ext.data.Store#autoLoad autoLoad}: {params:{start: 0, limit: 25}},
47595     ...
47596 });
47597  * </code></pre>
47598  * 
47599  * <p>The packet sent back from the server would have this form:</p>
47600  * <pre><code>
47601 {
47602     "success": true,
47603     "results": 2000, 
47604     "rows": [ // <b>*Note:</b> this must be an Array 
47605         { "id":  1, "name": "Bill", "occupation": "Gardener" },
47606         { "id":  2, "name":  "Ben", "occupation": "Horticulturalist" },
47607         ...
47608         { "id": 25, "name":  "Sue", "occupation": "Botanist" }
47609     ]
47610 }
47611  * </code></pre>
47612  * <p><u>Paging with Local Data</u></p>
47613  * <p>Paging can also be accomplished with local data using extensions:</p>
47614  * <div class="mdetail-params"><ul>
47615  * <li><a href="http://extjs.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
47616  * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
47617  * </ul></div>
47618  * @constructor Create a new PagingToolbar
47619  * @param {Object} config The config object
47620  * @xtype paging
47621  */
47622 (function() {
47623
47624 var T = Ext.Toolbar;
47625
47626 Ext.PagingToolbar = Ext.extend(Ext.Toolbar, {
47627     /**
47628      * @cfg {Ext.data.Store} store
47629      * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
47630      */
47631     /**
47632      * @cfg {Boolean} displayInfo
47633      * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
47634      */
47635     /**
47636      * @cfg {Number} pageSize
47637      * The number of records to display per page (defaults to <tt>20</tt>)
47638      */
47639     pageSize : 20,
47640     /**
47641      * @cfg {Boolean} prependButtons
47642      * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
47643      * Defaults to <tt>false</tt>.
47644      */
47645     /**
47646      * @cfg {String} displayMsg
47647      * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
47648      * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
47649      * that are replaced by the values for start, end and total respectively. These tokens should
47650      * be preserved when overriding this string if showing those values is desired.
47651      */
47652     displayMsg : 'Displaying {0} - {1} of {2}',
47653     /**
47654      * @cfg {String} emptyMsg
47655      * The message to display when no records are found (defaults to 'No data to display')
47656      */
47657     emptyMsg : 'No data to display',
47658     /**
47659      * @cfg {String} beforePageText
47660      * The text displayed before the input item (defaults to <tt>'Page'</tt>).
47661      */
47662     beforePageText : 'Page',
47663     /**
47664      * @cfg {String} afterPageText
47665      * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
47666      * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
47667      * total pages. This token should be preserved when overriding this string if showing the
47668      * total page count is desired.
47669      */
47670     afterPageText : 'of {0}',
47671     /**
47672      * @cfg {String} firstText
47673      * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
47674      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47675      */
47676     firstText : 'First Page',
47677     /**
47678      * @cfg {String} prevText
47679      * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
47680      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47681      */
47682     prevText : 'Previous Page',
47683     /**
47684      * @cfg {String} nextText
47685      * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
47686      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47687      */
47688     nextText : 'Next Page',
47689     /**
47690      * @cfg {String} lastText
47691      * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
47692      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47693      */
47694     lastText : 'Last Page',
47695     /**
47696      * @cfg {String} refreshText
47697      * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
47698      * <b>Note</b>: quick tips must be initialized for the quicktip to show.
47699      */
47700     refreshText : 'Refresh',
47701
47702     /**
47703      * <p><b>Deprecated</b>. <code>paramNames</code> should be set in the <b>data store</b>
47704      * (see {@link Ext.data.Store#paramNames}).</p>
47705      * <br><p>Object mapping of parameter names used for load calls, initially set to:</p>
47706      * <pre>{start: 'start', limit: 'limit'}</pre>
47707      * @type Object
47708      * @property paramNames
47709      * @deprecated
47710      */
47711
47712     /**
47713      * The number of records to display per page.  See also <tt>{@link #cursor}</tt>.
47714      * @type Number
47715      * @property pageSize
47716      */
47717
47718     /**
47719      * Indicator for the record position.  This property might be used to get the active page
47720      * number for example:<pre><code>
47721      * // t is reference to the paging toolbar instance
47722      * var activePage = Math.ceil((t.cursor + t.pageSize) / t.pageSize);
47723      * </code></pre>
47724      * @type Number
47725      * @property cursor
47726      */
47727
47728     initComponent : function(){
47729         var pagingItems = [this.first = new T.Button({
47730             tooltip: this.firstText,
47731             overflowText: this.firstText,
47732             iconCls: 'x-tbar-page-first',
47733             disabled: true,
47734             handler: this.moveFirst,
47735             scope: this
47736         }), this.prev = new T.Button({
47737             tooltip: this.prevText,
47738             overflowText: this.prevText,
47739             iconCls: 'x-tbar-page-prev',
47740             disabled: true,
47741             handler: this.movePrevious,
47742             scope: this
47743         }), '-', this.beforePageText,
47744         this.inputItem = new Ext.form.NumberField({
47745             cls: 'x-tbar-page-number',
47746             allowDecimals: false,
47747             allowNegative: false,
47748             enableKeyEvents: true,
47749             selectOnFocus: true,
47750             submitValue: false,
47751             listeners: {
47752                 scope: this,
47753                 keydown: this.onPagingKeyDown,
47754                 blur: this.onPagingBlur
47755             }
47756         }), this.afterTextItem = new T.TextItem({
47757             text: String.format(this.afterPageText, 1)
47758         }), '-', this.next = new T.Button({
47759             tooltip: this.nextText,
47760             overflowText: this.nextText,
47761             iconCls: 'x-tbar-page-next',
47762             disabled: true,
47763             handler: this.moveNext,
47764             scope: this
47765         }), this.last = new T.Button({
47766             tooltip: this.lastText,
47767             overflowText: this.lastText,
47768             iconCls: 'x-tbar-page-last',
47769             disabled: true,
47770             handler: this.moveLast,
47771             scope: this
47772         }), '-', this.refresh = new T.Button({
47773             tooltip: this.refreshText,
47774             overflowText: this.refreshText,
47775             iconCls: 'x-tbar-loading',
47776             handler: this.doRefresh,
47777             scope: this
47778         })];
47779
47780
47781         var userItems = this.items || this.buttons || [];
47782         if (this.prependButtons) {
47783             this.items = userItems.concat(pagingItems);
47784         }else{
47785             this.items = pagingItems.concat(userItems);
47786         }
47787         delete this.buttons;
47788         if(this.displayInfo){
47789             this.items.push('->');
47790             this.items.push(this.displayItem = new T.TextItem({}));
47791         }
47792         Ext.PagingToolbar.superclass.initComponent.call(this);
47793         this.addEvents(
47794             /**
47795              * @event change
47796              * Fires after the active page has been changed.
47797              * @param {Ext.PagingToolbar} this
47798              * @param {Object} pageData An object that has these properties:<ul>
47799              * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
47800              * returned by the server</div></li>
47801              * <li><code>activePage</code> : Number <div class="sub-desc">The current page number</div></li>
47802              * <li><code>pages</code> : Number <div class="sub-desc">The total number of pages (calculated from
47803              * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
47804              * </ul>
47805              */
47806             'change',
47807             /**
47808              * @event beforechange
47809              * Fires just before the active page is changed.
47810              * Return false to prevent the active page from being changed.
47811              * @param {Ext.PagingToolbar} this
47812              * @param {Object} params An object hash of the parameters which the PagingToolbar will send when
47813              * loading the required page. This will contain:<ul>
47814              * <li><code>start</code> : Number <div class="sub-desc">The starting row number for the next page of records to
47815              * be retrieved from the server</div></li>
47816              * <li><code>limit</code> : Number <div class="sub-desc">The number of records to be retrieved from the server</div></li>
47817              * </ul>
47818              * <p>(note: the names of the <b>start</b> and <b>limit</b> properties are determined
47819              * by the store's {@link Ext.data.Store#paramNames paramNames} property.)</p>
47820              * <p>Parameters may be added as required in the event handler.</p>
47821              */
47822             'beforechange'
47823         );
47824         this.on('afterlayout', this.onFirstLayout, this, {single: true});
47825         this.cursor = 0;
47826         this.bindStore(this.store, true);
47827     },
47828
47829     // private
47830     onFirstLayout : function(){
47831         if(this.dsLoaded){
47832             this.onLoad.apply(this, this.dsLoaded);
47833         }
47834     },
47835
47836     // private
47837     updateInfo : function(){
47838         if(this.displayItem){
47839             var count = this.store.getCount();
47840             var msg = count == 0 ?
47841                 this.emptyMsg :
47842                 String.format(
47843                     this.displayMsg,
47844                     this.cursor+1, this.cursor+count, this.store.getTotalCount()
47845                 );
47846             this.displayItem.setText(msg);
47847         }
47848     },
47849
47850     // private
47851     onLoad : function(store, r, o){
47852         if(!this.rendered){
47853             this.dsLoaded = [store, r, o];
47854             return;
47855         }
47856         var p = this.getParams();
47857         this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
47858         var d = this.getPageData(), ap = d.activePage, ps = d.pages;
47859
47860         this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
47861         this.inputItem.setValue(ap);
47862         this.first.setDisabled(ap == 1);
47863         this.prev.setDisabled(ap == 1);
47864         this.next.setDisabled(ap == ps);
47865         this.last.setDisabled(ap == ps);
47866         this.refresh.enable();
47867         this.updateInfo();
47868         this.fireEvent('change', this, d);
47869     },
47870
47871     // private
47872     getPageData : function(){
47873         var total = this.store.getTotalCount();
47874         return {
47875             total : total,
47876             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
47877             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
47878         };
47879     },
47880
47881     /**
47882      * Change the active page
47883      * @param {Integer} page The page to display
47884      */
47885     changePage : function(page){
47886         this.doLoad(((page-1) * this.pageSize).constrain(0, this.store.getTotalCount()));
47887     },
47888
47889     // private
47890     onLoadError : function(){
47891         if(!this.rendered){
47892             return;
47893         }
47894         this.refresh.enable();
47895     },
47896
47897     // private
47898     readPage : function(d){
47899         var v = this.inputItem.getValue(), pageNum;
47900         if (!v || isNaN(pageNum = parseInt(v, 10))) {
47901             this.inputItem.setValue(d.activePage);
47902             return false;
47903         }
47904         return pageNum;
47905     },
47906
47907     onPagingFocus : function(){
47908         this.inputItem.select();
47909     },
47910
47911     //private
47912     onPagingBlur : function(e){
47913         this.inputItem.setValue(this.getPageData().activePage);
47914     },
47915
47916     // private
47917     onPagingKeyDown : function(field, e){
47918         var k = e.getKey(), d = this.getPageData(), pageNum;
47919         if (k == e.RETURN) {
47920             e.stopEvent();
47921             pageNum = this.readPage(d);
47922             if(pageNum !== false){
47923                 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
47924                 this.doLoad(pageNum * this.pageSize);
47925             }
47926         }else if (k == e.HOME || k == e.END){
47927             e.stopEvent();
47928             pageNum = k == e.HOME ? 1 : d.pages;
47929             field.setValue(pageNum);
47930         }else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN){
47931             e.stopEvent();
47932             if((pageNum = this.readPage(d))){
47933                 var increment = e.shiftKey ? 10 : 1;
47934                 if(k == e.DOWN || k == e.PAGEDOWN){
47935                     increment *= -1;
47936                 }
47937                 pageNum += increment;
47938                 if(pageNum >= 1 & pageNum <= d.pages){
47939                     field.setValue(pageNum);
47940                 }
47941             }
47942         }
47943     },
47944
47945     // private
47946     getParams : function(){
47947         //retain backwards compat, allow params on the toolbar itself, if they exist.
47948         return this.paramNames || this.store.paramNames;
47949     },
47950
47951     // private
47952     beforeLoad : function(){
47953         if(this.rendered && this.refresh){
47954             this.refresh.disable();
47955         }
47956     },
47957
47958     // private
47959     doLoad : function(start){
47960         var o = {}, pn = this.getParams();
47961         o[pn.start] = start;
47962         o[pn.limit] = this.pageSize;
47963         if(this.fireEvent('beforechange', this, o) !== false){
47964             this.store.load({params:o});
47965         }
47966     },
47967
47968     /**
47969      * Move to the first page, has the same effect as clicking the 'first' button.
47970      */
47971     moveFirst : function(){
47972         this.doLoad(0);
47973     },
47974
47975     /**
47976      * Move to the previous page, has the same effect as clicking the 'previous' button.
47977      */
47978     movePrevious : function(){
47979         this.doLoad(Math.max(0, this.cursor-this.pageSize));
47980     },
47981
47982     /**
47983      * Move to the next page, has the same effect as clicking the 'next' button.
47984      */
47985     moveNext : function(){
47986         this.doLoad(this.cursor+this.pageSize);
47987     },
47988
47989     /**
47990      * Move to the last page, has the same effect as clicking the 'last' button.
47991      */
47992     moveLast : function(){
47993         var total = this.store.getTotalCount(),
47994             extra = total % this.pageSize;
47995
47996         this.doLoad(extra ? (total - extra) : total - this.pageSize);
47997     },
47998
47999     /**
48000      * Refresh the current page, has the same effect as clicking the 'refresh' button.
48001      */
48002     doRefresh : function(){
48003         this.doLoad(this.cursor);
48004     },
48005
48006     /**
48007      * Binds the paging toolbar to the specified {@link Ext.data.Store}
48008      * @param {Store} store The store to bind to this toolbar
48009      * @param {Boolean} initial (Optional) true to not remove listeners
48010      */
48011     bindStore : function(store, initial){
48012         var doLoad;
48013         if(!initial && this.store){
48014             if(store !== this.store && this.store.autoDestroy){
48015                 this.store.destroy();
48016             }else{
48017                 this.store.un('beforeload', this.beforeLoad, this);
48018                 this.store.un('load', this.onLoad, this);
48019                 this.store.un('exception', this.onLoadError, this);
48020             }
48021             if(!store){
48022                 this.store = null;
48023             }
48024         }
48025         if(store){
48026             store = Ext.StoreMgr.lookup(store);
48027             store.on({
48028                 scope: this,
48029                 beforeload: this.beforeLoad,
48030                 load: this.onLoad,
48031                 exception: this.onLoadError
48032             });
48033             doLoad = true;
48034         }
48035         this.store = store;
48036         if(doLoad){
48037             this.onLoad(store, null, {});
48038         }
48039     },
48040
48041     /**
48042      * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
48043      * @param {Ext.data.Store} store The data store to unbind
48044      */
48045     unbind : function(store){
48046         this.bindStore(null);
48047     },
48048
48049     /**
48050      * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
48051      * @param {Ext.data.Store} store The data store to bind
48052      */
48053     bind : function(store){
48054         this.bindStore(store);
48055     },
48056
48057     // private
48058     onDestroy : function(){
48059         this.bindStore(null);
48060         Ext.PagingToolbar.superclass.onDestroy.call(this);
48061     }
48062 });
48063
48064 })();
48065 Ext.reg('paging', Ext.PagingToolbar);/**
48066  * @class Ext.History
48067  * @extends Ext.util.Observable
48068  * History management component that allows you to register arbitrary tokens that signify application
48069  * history state on navigation actions.  You can then handle the history {@link #change} event in order
48070  * to reset your application UI to the appropriate state when the user navigates forward or backward through
48071  * the browser history stack.
48072  * @singleton
48073  */
48074 Ext.History = (function () {
48075     var iframe, hiddenField;
48076     var ready = false;
48077     var currentToken;
48078
48079     function getHash() {
48080         var href = top.location.href, i = href.indexOf("#");
48081         return i >= 0 ? href.substr(i + 1) : null;
48082     }
48083
48084     function doSave() {
48085         hiddenField.value = currentToken;
48086     }
48087
48088     function handleStateChange(token) {
48089         currentToken = token;
48090         Ext.History.fireEvent('change', token);
48091     }
48092
48093     function updateIFrame (token) {
48094         var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');
48095         try {
48096             var doc = iframe.contentWindow.document;
48097             doc.open();
48098             doc.write(html);
48099             doc.close();
48100             return true;
48101         } catch (e) {
48102             return false;
48103         }
48104     }
48105
48106     function checkIFrame() {
48107         if (!iframe.contentWindow || !iframe.contentWindow.document) {
48108             setTimeout(checkIFrame, 10);
48109             return;
48110         }
48111
48112         var doc = iframe.contentWindow.document;
48113         var elem = doc.getElementById("state");
48114         var token = elem ? elem.innerText : null;
48115
48116         var hash = getHash();
48117
48118         setInterval(function () {
48119
48120             doc = iframe.contentWindow.document;
48121             elem = doc.getElementById("state");
48122
48123             var newtoken = elem ? elem.innerText : null;
48124
48125             var newHash = getHash();
48126
48127             if (newtoken !== token) {
48128                 token = newtoken;
48129                 handleStateChange(token);
48130                 top.location.hash = token;
48131                 hash = token;
48132                 doSave();
48133             } else if (newHash !== hash) {
48134                 hash = newHash;
48135                 updateIFrame(newHash);
48136             }
48137
48138         }, 50);
48139
48140         ready = true;
48141
48142         Ext.History.fireEvent('ready', Ext.History);
48143     }
48144
48145     function startUp() {
48146         currentToken = hiddenField.value ? hiddenField.value : getHash();
48147
48148         if (Ext.isIE) {
48149             checkIFrame();
48150         } else {
48151             var hash = getHash();
48152             setInterval(function () {
48153                 var newHash = getHash();
48154                 if (newHash !== hash) {
48155                     hash = newHash;
48156                     handleStateChange(hash);
48157                     doSave();
48158                 }
48159             }, 50);
48160             ready = true;
48161             Ext.History.fireEvent('ready', Ext.History);
48162         }
48163     }
48164
48165     return {
48166         /**
48167          * The id of the hidden field required for storing the current history token.
48168          * @type String
48169          * @property
48170          */
48171         fieldId: 'x-history-field',
48172         /**
48173          * The id of the iframe required by IE to manage the history stack.
48174          * @type String
48175          * @property
48176          */
48177         iframeId: 'x-history-frame',
48178
48179         events:{},
48180
48181         /**
48182          * Initialize the global History instance.
48183          * @param {Boolean} onReady (optional) A callback function that will be called once the history
48184          * component is fully initialized.
48185          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.
48186          */
48187         init: function (onReady, scope) {
48188             if(ready) {
48189                 Ext.callback(onReady, scope, [this]);
48190                 return;
48191             }
48192             if(!Ext.isReady){
48193                 Ext.onReady(function(){
48194                     Ext.History.init(onReady, scope);
48195                 });
48196                 return;
48197             }
48198             hiddenField = Ext.getDom(Ext.History.fieldId);
48199             if (Ext.isIE) {
48200                 iframe = Ext.getDom(Ext.History.iframeId);
48201             }
48202             this.addEvents(
48203                 /**
48204                  * @event ready
48205                  * Fires when the Ext.History singleton has been initialized and is ready for use.
48206                  * @param {Ext.History} The Ext.History singleton.
48207                  */
48208                 'ready',
48209                 /**
48210                  * @event change
48211                  * Fires when navigation back or forwards within the local page's history occurs.
48212                  * @param {String} token An identifier associated with the page state at that point in its history.
48213                  */
48214                 'change'
48215             );
48216             if(onReady){
48217                 this.on('ready', onReady, scope, {single:true});
48218             }
48219             startUp();
48220         },
48221
48222         /**
48223          * Add a new token to the history stack. This can be any arbitrary value, although it would
48224          * commonly be the concatenation of a component id and another id marking the specifc history
48225          * state of that component.  Example usage:
48226          * <pre><code>
48227 // Handle tab changes on a TabPanel
48228 tabPanel.on('tabchange', function(tabPanel, tab){
48229     Ext.History.add(tabPanel.id + ':' + tab.id);
48230 });
48231 </code></pre>
48232          * @param {String} token The value that defines a particular application-specific history state
48233          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token
48234          * it will not save a new history step. Set to false if the same state can be saved more than once
48235          * at the same history stack location (defaults to true).
48236          */
48237         add: function (token, preventDup) {
48238             if(preventDup !== false){
48239                 if(this.getToken() == token){
48240                     return true;
48241                 }
48242             }
48243             if (Ext.isIE) {
48244                 return updateIFrame(token);
48245             } else {
48246                 top.location.hash = token;
48247                 return true;
48248             }
48249         },
48250
48251         /**
48252          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
48253          */
48254         back: function(){
48255             history.go(-1);
48256         },
48257
48258         /**
48259          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
48260          */
48261         forward: function(){
48262             history.go(1);
48263         },
48264
48265         /**
48266          * Retrieves the currently-active history token.
48267          * @return {String} The token
48268          */
48269         getToken: function() {
48270             return ready ? currentToken : getHash();
48271         }
48272     };
48273 })();
48274 Ext.apply(Ext.History, new Ext.util.Observable());/**
48275  * @class Ext.Tip
48276  * @extends Ext.Panel
48277  * @xtype tip
48278  * This is the base class for {@link Ext.QuickTip} and {@link Ext.Tooltip} that provides the basic layout and
48279  * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned
48280  * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.
48281  * @constructor
48282  * Create a new Tip
48283  * @param {Object} config The configuration options
48284  */
48285 Ext.Tip = Ext.extend(Ext.Panel, {
48286     /**
48287      * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).
48288      */
48289     /**
48290      * @cfg {Number} width
48291      * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
48292      * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.
48293      */
48294     /**
48295      * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).
48296      */
48297     minWidth : 40,
48298     /**
48299      * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.
48300      */
48301     maxWidth : 300,
48302     /**
48303      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
48304      * for bottom-right shadow (defaults to "sides").
48305      */
48306     shadow : "sides",
48307     /**
48308      * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.Element#alignTo} anchor position value
48309      * for this tip relative to its element of origin (defaults to "tl-bl?").
48310      */
48311     defaultAlign : "tl-bl?",
48312     autoRender: true,
48313     quickShowInterval : 250,
48314
48315     // private panel overrides
48316     frame:true,
48317     hidden:true,
48318     baseCls: 'x-tip',
48319     floating:{shadow:true,shim:true,useDisplay:true,constrain:false},
48320     autoHeight:true,
48321
48322     closeAction: 'hide',
48323
48324     // private
48325     initComponent : function(){
48326         Ext.Tip.superclass.initComponent.call(this);
48327         if(this.closable && !this.title){
48328             this.elements += ',header';
48329         }
48330     },
48331
48332     // private
48333     afterRender : function(){
48334         Ext.Tip.superclass.afterRender.call(this);
48335         if(this.closable){
48336             this.addTool({
48337                 id: 'close',
48338                 handler: this[this.closeAction],
48339                 scope: this
48340             });
48341         }
48342     },
48343
48344     /**
48345      * Shows this tip at the specified XY position.  Example usage:
48346      * <pre><code>
48347 // Show the tip at x:50 and y:100
48348 tip.showAt([50,100]);
48349 </code></pre>
48350      * @param {Array} xy An array containing the x and y coordinates
48351      */
48352     showAt : function(xy){
48353         Ext.Tip.superclass.show.call(this);
48354         if(this.measureWidth !== false && (!this.initialConfig || typeof this.initialConfig.width != 'number')){
48355             this.doAutoWidth();
48356         }
48357         if(this.constrainPosition){
48358             xy = this.el.adjustForConstraints(xy);
48359         }
48360         this.setPagePosition(xy[0], xy[1]);
48361     },
48362
48363     // protected
48364     doAutoWidth : function(adjust){
48365         adjust = adjust || 0;
48366         var bw = this.body.getTextWidth();
48367         if(this.title){
48368             bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
48369         }
48370         bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + adjust;
48371         this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
48372         
48373         // IE7 repaint bug on initial show
48374         if(Ext.isIE7 && !this.repainted){
48375             this.el.repaint();
48376             this.repainted = true;
48377         }
48378     },
48379
48380     /**
48381      * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.Element#alignTo}
48382      * anchor position value.  Example usage:
48383      * <pre><code>
48384 // Show the tip at the default position ('tl-br?')
48385 tip.showBy('my-el');
48386
48387 // Show the tip's top-left corner anchored to the element's top-right corner
48388 tip.showBy('my-el', 'tl-tr');
48389 </code></pre>
48390      * @param {Mixed} el An HTMLElement, Ext.Element or string id of the target element to align to
48391      * @param {String} position (optional) A valid {@link Ext.Element#alignTo} anchor position (defaults to 'tl-br?' or
48392      * {@link #defaultAlign} if specified).
48393      */
48394     showBy : function(el, pos){
48395         if(!this.rendered){
48396             this.render(Ext.getBody());
48397         }
48398         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));
48399     },
48400
48401     initDraggable : function(){
48402         this.dd = new Ext.Tip.DD(this, typeof this.draggable == 'boolean' ? null : this.draggable);
48403         this.header.addClass('x-tip-draggable');
48404     }
48405 });
48406
48407 Ext.reg('tip', Ext.Tip);
48408
48409 // private - custom Tip DD implementation
48410 Ext.Tip.DD = function(tip, config){
48411     Ext.apply(this, config);
48412     this.tip = tip;
48413     Ext.Tip.DD.superclass.constructor.call(this, tip.el.id, 'WindowDD-'+tip.id);
48414     this.setHandleElId(tip.header.id);
48415     this.scroll = false;
48416 };
48417
48418 Ext.extend(Ext.Tip.DD, Ext.dd.DD, {
48419     moveOnly:true,
48420     scroll:false,
48421     headerOffsets:[100, 25],
48422     startDrag : function(){
48423         this.tip.el.disableShadow();
48424     },
48425     endDrag : function(e){
48426         this.tip.el.enableShadow(true);
48427     }
48428 });/**
48429  * @class Ext.ToolTip
48430  * @extends Ext.Tip
48431  * A standard tooltip implementation for providing additional information when hovering over a target element.
48432  * @xtype tooltip
48433  * @constructor
48434  * Create a new Tooltip
48435  * @param {Object} config The configuration options
48436  */
48437 Ext.ToolTip = Ext.extend(Ext.Tip, {
48438     /**
48439      * When a Tooltip is configured with the <code>{@link #delegate}</code>
48440      * option to cause selected child elements of the <code>{@link #target}</code>
48441      * Element to each trigger a seperate show event, this property is set to
48442      * the DOM element which triggered the show.
48443      * @type DOMElement
48444      * @property triggerElement
48445      */
48446     /**
48447      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to monitor
48448      * for mouseover events to trigger showing this ToolTip.
48449      */
48450     /**
48451      * @cfg {Boolean} autoHide True to automatically hide the tooltip after the
48452      * mouse exits the target element or after the <code>{@link #dismissDelay}</code>
48453      * has expired if set (defaults to true).  If <code>{@link closable} = true</code>
48454      * a close tool button will be rendered into the tooltip header.
48455      */
48456     /**
48457      * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays
48458      * after the mouse enters the target element (defaults to 500)
48459      */
48460     showDelay : 500,
48461     /**
48462      * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the
48463      * target element but before the tooltip actually hides (defaults to 200).
48464      * Set to 0 for the tooltip to hide immediately.
48465      */
48466     hideDelay : 200,
48467     /**
48468      * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip
48469      * automatically hides (defaults to 5000). To disable automatic hiding, set
48470      * dismissDelay = 0.
48471      */
48472     dismissDelay : 5000,
48473     /**
48474      * @cfg {Array} mouseOffset An XY offset from the mouse position where the
48475      * tooltip should be shown (defaults to [15,18]).
48476      */
48477     /**
48478      * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it
48479      * moves over the target element (defaults to false).
48480      */
48481     trackMouse : false,
48482     /**
48483      * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target
48484      * element, false to anchor it relative to the mouse coordinates (defaults
48485      * to true).  When <code>anchorToTarget</code> is true, use
48486      * <code>{@link #defaultAlign}</code> to control tooltip alignment to the
48487      * target element.  When <code>anchorToTarget</code> is false, use
48488      * <code>{@link #anchorPosition}</code> instead to control alignment.
48489      */
48490     anchorToTarget : true,
48491     /**
48492      * @cfg {Number} anchorOffset A numeric pixel value used to offset the
48493      * default position of the anchor arrow (defaults to 0).  When the anchor
48494      * position is on the top or bottom of the tooltip, <code>anchorOffset</code>
48495      * will be used as a horizontal offset.  Likewise, when the anchor position
48496      * is on the left or right side, <code>anchorOffset</code> will be used as
48497      * a vertical offset.
48498      */
48499     anchorOffset : 0,
48500     /**
48501      * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery}
48502      * selector which allows selection of individual elements within the
48503      * <code>{@link #target}</code> element to trigger showing and hiding the
48504      * ToolTip as the mouse moves within the target.</p>
48505      * <p>When specified, the child element of the target which caused a show
48506      * event is placed into the <code>{@link #triggerElement}</code> property
48507      * before the ToolTip is shown.</p>
48508      * <p>This may be useful when a Component has regular, repeating elements
48509      * in it, each of which need a Tooltip which contains information specific
48510      * to that element. For example:</p><pre><code>
48511 var myGrid = new Ext.grid.gridPanel(gridConfig);
48512 myGrid.on('render', function(grid) {
48513     var store = grid.getStore();  // Capture the Store.
48514     var view = grid.getView();    // Capture the GridView.
48515     myGrid.tip = new Ext.ToolTip({
48516         target: view.mainBody,    // The overall target element.
48517         delegate: '.x-grid3-row', // Each grid row causes its own seperate show and hide.
48518         trackMouse: true,         // Moving within the row should not hide the tip.
48519         renderTo: document.body,  // Render immediately so that tip.body can be
48520                                   //  referenced prior to the first show.
48521         listeners: {              // Change content dynamically depending on which element
48522                                   //  triggered the show.
48523             beforeshow: function updateTipBody(tip) {
48524                 var rowIndex = view.findRowIndex(tip.triggerElement);
48525                 tip.body.dom.innerHTML = 'Over Record ID ' + store.getAt(rowIndex).id;
48526             }
48527         }
48528     });
48529 });
48530      *</code></pre>
48531      */
48532
48533     // private
48534     targetCounter : 0,
48535
48536     constrainPosition : false,
48537
48538     // private
48539     initComponent : function(){
48540         Ext.ToolTip.superclass.initComponent.call(this);
48541         this.lastActive = new Date();
48542         this.initTarget(this.target);
48543         this.origAnchor = this.anchor;
48544     },
48545
48546     // private
48547     onRender : function(ct, position){
48548         Ext.ToolTip.superclass.onRender.call(this, ct, position);
48549         this.anchorCls = 'x-tip-anchor-' + this.getAnchorPosition();
48550         this.anchorEl = this.el.createChild({
48551             cls: 'x-tip-anchor ' + this.anchorCls
48552         });
48553     },
48554
48555     // private
48556     afterRender : function(){
48557         Ext.ToolTip.superclass.afterRender.call(this);
48558         this.anchorEl.setStyle('z-index', this.el.getZIndex() + 1);
48559     },
48560
48561     /**
48562      * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.
48563      * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to
48564      */
48565     initTarget : function(target){
48566         var t;
48567         if((t = Ext.get(target))){
48568             if(this.target){
48569                 var tg = Ext.get(this.target);
48570                 this.mun(tg, 'mouseover', this.onTargetOver, this);
48571                 this.mun(tg, 'mouseout', this.onTargetOut, this);
48572                 this.mun(tg, 'mousemove', this.onMouseMove, this);
48573             }
48574             this.mon(t, {
48575                 mouseover: this.onTargetOver,
48576                 mouseout: this.onTargetOut,
48577                 mousemove: this.onMouseMove,
48578                 scope: this
48579             });
48580             this.target = t;
48581         }
48582         if(this.anchor){
48583             this.anchorTarget = this.target;
48584         }
48585     },
48586
48587     // private
48588     onMouseMove : function(e){
48589         var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
48590         if (t) {
48591             this.targetXY = e.getXY();
48592             if (t === this.triggerElement) {
48593                 if(!this.hidden && this.trackMouse){
48594                     this.setPagePosition(this.getTargetXY());
48595                 }
48596             } else {
48597                 this.hide();
48598                 this.lastActive = new Date(0);
48599                 this.onTargetOver(e);
48600             }
48601         } else if (!this.closable && this.isVisible()) {
48602             this.hide();
48603         }
48604     },
48605
48606     // private
48607     getTargetXY : function(){
48608         if(this.delegate){
48609             this.anchorTarget = this.triggerElement;
48610         }
48611         if(this.anchor){
48612             this.targetCounter++;
48613             var offsets = this.getOffsets(),
48614                 xy = (this.anchorToTarget && !this.trackMouse) ? this.el.getAlignToXY(this.anchorTarget, this.getAnchorAlign()) : this.targetXY,
48615                 dw = Ext.lib.Dom.getViewWidth() - 5,
48616                 dh = Ext.lib.Dom.getViewHeight() - 5,
48617                 de = document.documentElement,
48618                 bd = document.body,
48619                 scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5,
48620                 scrollY = (de.scrollTop || bd.scrollTop || 0) + 5,
48621                 axy = [xy[0] + offsets[0], xy[1] + offsets[1]],
48622                 sz = this.getSize();
48623                 
48624             this.anchorEl.removeClass(this.anchorCls);
48625
48626             if(this.targetCounter < 2){
48627                 if(axy[0] < scrollX){
48628                     if(this.anchorToTarget){
48629                         this.defaultAlign = 'l-r';
48630                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}
48631                     }
48632                     this.anchor = 'left';
48633                     return this.getTargetXY();
48634                 }
48635                 if(axy[0]+sz.width > dw){
48636                     if(this.anchorToTarget){
48637                         this.defaultAlign = 'r-l';
48638                         if(this.mouseOffset){this.mouseOffset[0] *= -1;}
48639                     }
48640                     this.anchor = 'right';
48641                     return this.getTargetXY();
48642                 }
48643                 if(axy[1] < scrollY){
48644                     if(this.anchorToTarget){
48645                         this.defaultAlign = 't-b';
48646                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}
48647                     }
48648                     this.anchor = 'top';
48649                     return this.getTargetXY();
48650                 }
48651                 if(axy[1]+sz.height > dh){
48652                     if(this.anchorToTarget){
48653                         this.defaultAlign = 'b-t';
48654                         if(this.mouseOffset){this.mouseOffset[1] *= -1;}
48655                     }
48656                     this.anchor = 'bottom';
48657                     return this.getTargetXY();
48658                 }
48659             }
48660
48661             this.anchorCls = 'x-tip-anchor-'+this.getAnchorPosition();
48662             this.anchorEl.addClass(this.anchorCls);
48663             this.targetCounter = 0;
48664             return axy;
48665         }else{
48666             var mouseOffset = this.getMouseOffset();
48667             return [this.targetXY[0]+mouseOffset[0], this.targetXY[1]+mouseOffset[1]];
48668         }
48669     },
48670
48671     getMouseOffset : function(){
48672         var offset = this.anchor ? [0,0] : [15,18];
48673         if(this.mouseOffset){
48674             offset[0] += this.mouseOffset[0];
48675             offset[1] += this.mouseOffset[1];
48676         }
48677         return offset;
48678     },
48679
48680     // private
48681     getAnchorPosition : function(){
48682         if(this.anchor){
48683             this.tipAnchor = this.anchor.charAt(0);
48684         }else{
48685             var m = this.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
48686             if(!m){
48687                throw 'AnchorTip.defaultAlign is invalid';
48688             }
48689             this.tipAnchor = m[1].charAt(0);
48690         }
48691
48692         switch(this.tipAnchor){
48693             case 't': return 'top';
48694             case 'b': return 'bottom';
48695             case 'r': return 'right';
48696         }
48697         return 'left';
48698     },
48699
48700     // private
48701     getAnchorAlign : function(){
48702         switch(this.anchor){
48703             case 'top'  : return 'tl-bl';
48704             case 'left' : return 'tl-tr';
48705             case 'right': return 'tr-tl';
48706             default     : return 'bl-tl';
48707         }
48708     },
48709
48710     // private
48711     getOffsets : function(){
48712         var offsets, 
48713             ap = this.getAnchorPosition().charAt(0);
48714         if(this.anchorToTarget && !this.trackMouse){
48715             switch(ap){
48716                 case 't':
48717                     offsets = [0, 9];
48718                     break;
48719                 case 'b':
48720                     offsets = [0, -13];
48721                     break;
48722                 case 'r':
48723                     offsets = [-13, 0];
48724                     break;
48725                 default:
48726                     offsets = [9, 0];
48727                     break;
48728             }
48729         }else{
48730             switch(ap){
48731                 case 't':
48732                     offsets = [-15-this.anchorOffset, 30];
48733                     break;
48734                 case 'b':
48735                     offsets = [-19-this.anchorOffset, -13-this.el.dom.offsetHeight];
48736                     break;
48737                 case 'r':
48738                     offsets = [-15-this.el.dom.offsetWidth, -13-this.anchorOffset];
48739                     break;
48740                 default:
48741                     offsets = [25, -13-this.anchorOffset];
48742                     break;
48743             }
48744         }
48745         var mouseOffset = this.getMouseOffset();
48746         offsets[0] += mouseOffset[0];
48747         offsets[1] += mouseOffset[1];
48748
48749         return offsets;
48750     },
48751
48752     // private
48753     onTargetOver : function(e){
48754         if(this.disabled || e.within(this.target.dom, true)){
48755             return;
48756         }
48757         var t = e.getTarget(this.delegate);
48758         if (t) {
48759             this.triggerElement = t;
48760             this.clearTimer('hide');
48761             this.targetXY = e.getXY();
48762             this.delayShow();
48763         }
48764     },
48765
48766     // private
48767     delayShow : function(){
48768         if(this.hidden && !this.showTimer){
48769             if(this.lastActive.getElapsed() < this.quickShowInterval){
48770                 this.show();
48771             }else{
48772                 this.showTimer = this.show.defer(this.showDelay, this);
48773             }
48774         }else if(!this.hidden && this.autoHide !== false){
48775             this.show();
48776         }
48777     },
48778
48779     // private
48780     onTargetOut : function(e){
48781         if(this.disabled || e.within(this.target.dom, true)){
48782             return;
48783         }
48784         this.clearTimer('show');
48785         if(this.autoHide !== false){
48786             this.delayHide();
48787         }
48788     },
48789
48790     // private
48791     delayHide : function(){
48792         if(!this.hidden && !this.hideTimer){
48793             this.hideTimer = this.hide.defer(this.hideDelay, this);
48794         }
48795     },
48796
48797     /**
48798      * Hides this tooltip if visible.
48799      */
48800     hide: function(){
48801         this.clearTimer('dismiss');
48802         this.lastActive = new Date();
48803         if(this.anchorEl){
48804             this.anchorEl.hide();
48805         }
48806         Ext.ToolTip.superclass.hide.call(this);
48807         delete this.triggerElement;
48808     },
48809
48810     /**
48811      * Shows this tooltip at the current event target XY position.
48812      */
48813     show : function(){
48814         if(this.anchor){
48815             // pre-show it off screen so that the el will have dimensions
48816             // for positioning calcs when getting xy next
48817             this.showAt([-1000,-1000]);
48818             this.origConstrainPosition = this.constrainPosition;
48819             this.constrainPosition = false;
48820             this.anchor = this.origAnchor;
48821         }
48822         this.showAt(this.getTargetXY());
48823
48824         if(this.anchor){
48825             this.syncAnchor();
48826             this.anchorEl.show();
48827             this.constrainPosition = this.origConstrainPosition;
48828         }else{
48829             this.anchorEl.hide();
48830         }
48831     },
48832
48833     // inherit docs
48834     showAt : function(xy){
48835         this.lastActive = new Date();
48836         this.clearTimers();
48837         Ext.ToolTip.superclass.showAt.call(this, xy);
48838         if(this.dismissDelay && this.autoHide !== false){
48839             this.dismissTimer = this.hide.defer(this.dismissDelay, this);
48840         }
48841         if(this.anchor && !this.anchorEl.isVisible()){
48842             this.syncAnchor();
48843             this.anchorEl.show();
48844         }
48845     },
48846
48847     // private
48848     syncAnchor : function(){
48849         var anchorPos, targetPos, offset;
48850         switch(this.tipAnchor.charAt(0)){
48851             case 't':
48852                 anchorPos = 'b';
48853                 targetPos = 'tl';
48854                 offset = [20+this.anchorOffset, 2];
48855                 break;
48856             case 'r':
48857                 anchorPos = 'l';
48858                 targetPos = 'tr';
48859                 offset = [-2, 11+this.anchorOffset];
48860                 break;
48861             case 'b':
48862                 anchorPos = 't';
48863                 targetPos = 'bl';
48864                 offset = [20+this.anchorOffset, -2];
48865                 break;
48866             default:
48867                 anchorPos = 'r';
48868                 targetPos = 'tl';
48869                 offset = [2, 11+this.anchorOffset];
48870                 break;
48871         }
48872         this.anchorEl.alignTo(this.el, anchorPos+'-'+targetPos, offset);
48873     },
48874
48875     // private
48876     setPagePosition : function(x, y){
48877         Ext.ToolTip.superclass.setPagePosition.call(this, x, y);
48878         if(this.anchor){
48879             this.syncAnchor();
48880         }
48881     },
48882
48883     // private
48884     clearTimer : function(name){
48885         name = name + 'Timer';
48886         clearTimeout(this[name]);
48887         delete this[name];
48888     },
48889
48890     // private
48891     clearTimers : function(){
48892         this.clearTimer('show');
48893         this.clearTimer('dismiss');
48894         this.clearTimer('hide');
48895     },
48896
48897     // private
48898     onShow : function(){
48899         Ext.ToolTip.superclass.onShow.call(this);
48900         Ext.getDoc().on('mousedown', this.onDocMouseDown, this);
48901     },
48902
48903     // private
48904     onHide : function(){
48905         Ext.ToolTip.superclass.onHide.call(this);
48906         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
48907     },
48908
48909     // private
48910     onDocMouseDown : function(e){
48911         if(this.autoHide !== true && !this.closable && !e.within(this.el.dom)){
48912             this.disable();
48913             this.doEnable.defer(100, this);
48914         }
48915     },
48916     
48917     // private
48918     doEnable : function(){
48919         if(!this.isDestroyed){
48920             this.enable();
48921         }
48922     },
48923
48924     // private
48925     onDisable : function(){
48926         this.clearTimers();
48927         this.hide();
48928     },
48929
48930     // private
48931     adjustPosition : function(x, y){
48932         if(this.contstrainPosition){
48933             var ay = this.targetXY[1], h = this.getSize().height;
48934             if(y <= ay && (y+h) >= ay){
48935                 y = ay-h-5;
48936             }
48937         }
48938         return {x : x, y: y};
48939     },
48940     
48941     beforeDestroy : function(){
48942         this.clearTimers();
48943         Ext.destroy(this.anchorEl);
48944         delete this.anchorEl;
48945         delete this.target;
48946         delete this.anchorTarget;
48947         delete this.triggerElement;
48948         Ext.ToolTip.superclass.beforeDestroy.call(this);    
48949     },
48950
48951     // private
48952     onDestroy : function(){
48953         Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
48954         Ext.ToolTip.superclass.onDestroy.call(this);
48955     }
48956 });
48957
48958 Ext.reg('tooltip', Ext.ToolTip);/**
48959  * @class Ext.QuickTip
48960  * @extends Ext.ToolTip
48961  * @xtype quicktip
48962  * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global
48963  * {@link Ext.QuickTips} instance.  See the QuickTips class header for additional usage details and examples.
48964  * @constructor
48965  * Create a new Tip
48966  * @param {Object} config The configuration options
48967  */
48968 Ext.QuickTip = Ext.extend(Ext.ToolTip, {
48969     /**
48970      * @cfg {Mixed} target The target HTMLElement, Ext.Element or id to associate with this quicktip (defaults to the document).
48971      */
48972     /**
48973      * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).
48974      */
48975     interceptTitles : false,
48976
48977     // private
48978     tagConfig : {
48979         namespace : "ext",
48980         attribute : "qtip",
48981         width : "qwidth",
48982         target : "target",
48983         title : "qtitle",
48984         hide : "hide",
48985         cls : "qclass",
48986         align : "qalign",
48987         anchor : "anchor"
48988     },
48989
48990     // private
48991     initComponent : function(){
48992         this.target = this.target || Ext.getDoc();
48993         this.targets = this.targets || {};
48994         Ext.QuickTip.superclass.initComponent.call(this);
48995     },
48996
48997     /**
48998      * Configures a new quick tip instance and assigns it to a target element.  The following config values are
48999      * supported (for example usage, see the {@link Ext.QuickTips} class header):
49000      * <div class="mdetail-params"><ul>
49001      * <li>autoHide</li>
49002      * <li>cls</li>
49003      * <li>dismissDelay (overrides the singleton value)</li>
49004      * <li>target (required)</li>
49005      * <li>text (required)</li>
49006      * <li>title</li>
49007      * <li>width</li></ul></div>
49008      * @param {Object} config The config object
49009      */
49010     register : function(config){
49011         var cs = Ext.isArray(config) ? config : arguments;
49012         for(var i = 0, len = cs.length; i < len; i++){
49013             var c = cs[i];
49014             var target = c.target;
49015             if(target){
49016                 if(Ext.isArray(target)){
49017                     for(var j = 0, jlen = target.length; j < jlen; j++){
49018                         this.targets[Ext.id(target[j])] = c;
49019                     }
49020                 } else{
49021                     this.targets[Ext.id(target)] = c;
49022                 }
49023             }
49024         }
49025     },
49026
49027     /**
49028      * Removes this quick tip from its element and destroys it.
49029      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
49030      */
49031     unregister : function(el){
49032         delete this.targets[Ext.id(el)];
49033     },
49034     
49035     /**
49036      * Hides a visible tip or cancels an impending show for a particular element.
49037      * @param {String/HTMLElement/Element} el The element that is the target of the tip.
49038      */
49039     cancelShow: function(el){
49040         var at = this.activeTarget;
49041         el = Ext.get(el).dom;
49042         if(this.isVisible()){
49043             if(at && at.el == el){
49044                 this.hide();
49045             }
49046         }else if(at && at.el == el){
49047             this.clearTimer('show');
49048         }
49049     },
49050     
49051     getTipCfg: function(e) {
49052         var t = e.getTarget(), 
49053             ttp, 
49054             cfg;
49055         if(this.interceptTitles && t.title && Ext.isString(t.title)){
49056             ttp = t.title;
49057             t.qtip = ttp;
49058             t.removeAttribute("title");
49059             e.preventDefault();
49060         }else{
49061             cfg = this.tagConfig;
49062             ttp = t.qtip || Ext.fly(t).getAttribute(cfg.attribute, cfg.namespace);
49063         }
49064         return ttp;
49065     },
49066
49067     // private
49068     onTargetOver : function(e){
49069         if(this.disabled){
49070             return;
49071         }
49072         this.targetXY = e.getXY();
49073         var t = e.getTarget();
49074         if(!t || t.nodeType !== 1 || t == document || t == document.body){
49075             return;
49076         }
49077         if(this.activeTarget && ((t == this.activeTarget.el) || Ext.fly(this.activeTarget.el).contains(t))){
49078             this.clearTimer('hide');
49079             this.show();
49080             return;
49081         }
49082         if(t && this.targets[t.id]){
49083             this.activeTarget = this.targets[t.id];
49084             this.activeTarget.el = t;
49085             this.anchor = this.activeTarget.anchor;
49086             if(this.anchor){
49087                 this.anchorTarget = t;
49088             }
49089             this.delayShow();
49090             return;
49091         }
49092         var ttp, et = Ext.fly(t), cfg = this.tagConfig, ns = cfg.namespace;
49093         if(ttp = this.getTipCfg(e)){
49094             var autoHide = et.getAttribute(cfg.hide, ns);
49095             this.activeTarget = {
49096                 el: t,
49097                 text: ttp,
49098                 width: et.getAttribute(cfg.width, ns),
49099                 autoHide: autoHide != "user" && autoHide !== 'false',
49100                 title: et.getAttribute(cfg.title, ns),
49101                 cls: et.getAttribute(cfg.cls, ns),
49102                 align: et.getAttribute(cfg.align, ns)
49103                 
49104             };
49105             this.anchor = et.getAttribute(cfg.anchor, ns);
49106             if(this.anchor){
49107                 this.anchorTarget = t;
49108             }
49109             this.delayShow();
49110         }
49111     },
49112
49113     // private
49114     onTargetOut : function(e){
49115
49116         // If moving within the current target, and it does not have a new tip, ignore the mouseout
49117         if (this.activeTarget && e.within(this.activeTarget.el) && !this.getTipCfg(e)) {
49118             return;
49119         }
49120
49121         this.clearTimer('show');
49122         if(this.autoHide !== false){
49123             this.delayHide();
49124         }
49125     },
49126
49127     // inherit docs
49128     showAt : function(xy){
49129         var t = this.activeTarget;
49130         if(t){
49131             if(!this.rendered){
49132                 this.render(Ext.getBody());
49133                 this.activeTarget = t;
49134             }
49135             if(t.width){
49136                 this.setWidth(t.width);
49137                 this.body.setWidth(this.adjustBodyWidth(t.width - this.getFrameWidth()));
49138                 this.measureWidth = false;
49139             } else{
49140                 this.measureWidth = true;
49141             }
49142             this.setTitle(t.title || '');
49143             this.body.update(t.text);
49144             this.autoHide = t.autoHide;
49145             this.dismissDelay = t.dismissDelay || this.dismissDelay;
49146             if(this.lastCls){
49147                 this.el.removeClass(this.lastCls);
49148                 delete this.lastCls;
49149             }
49150             if(t.cls){
49151                 this.el.addClass(t.cls);
49152                 this.lastCls = t.cls;
49153             }
49154             if(this.anchor){
49155                 this.constrainPosition = false;
49156             }else if(t.align){ // TODO: this doesn't seem to work consistently
49157                 xy = this.el.getAlignToXY(t.el, t.align);
49158                 this.constrainPosition = false;
49159             }else{
49160                 this.constrainPosition = true;
49161             }
49162         }
49163         Ext.QuickTip.superclass.showAt.call(this, xy);
49164     },
49165
49166     // inherit docs
49167     hide: function(){
49168         delete this.activeTarget;
49169         Ext.QuickTip.superclass.hide.call(this);
49170     }
49171 });
49172 Ext.reg('quicktip', Ext.QuickTip);/**
49173  * @class Ext.QuickTips
49174  * <p>Provides attractive and customizable tooltips for any element. The QuickTips
49175  * singleton is used to configure and manage tooltips globally for multiple elements
49176  * in a generic manner.  To create individual tooltips with maximum customizability,
49177  * you should consider either {@link Ext.Tip} or {@link Ext.ToolTip}.</p>
49178  * <p>Quicktips can be configured via tag attributes directly in markup, or by
49179  * registering quick tips programmatically via the {@link #register} method.</p>
49180  * <p>The singleton's instance of {@link Ext.QuickTip} is available via
49181  * {@link #getQuickTip}, and supports all the methods, and all the all the
49182  * configuration properties of Ext.QuickTip. These settings will apply to all
49183  * tooltips shown by the singleton.</p>
49184  * <p>Below is the summary of the configuration properties which can be used.
49185  * For detailed descriptions see {@link #getQuickTip}</p>
49186  * <p><b>QuickTips singleton configs (all are optional)</b></p>
49187  * <div class="mdetail-params"><ul><li>dismissDelay</li>
49188  * <li>hideDelay</li>
49189  * <li>maxWidth</li>
49190  * <li>minWidth</li>
49191  * <li>showDelay</li>
49192  * <li>trackMouse</li></ul></div>
49193  * <p><b>Target element configs (optional unless otherwise noted)</b></p>
49194  * <div class="mdetail-params"><ul><li>autoHide</li>
49195  * <li>cls</li>
49196  * <li>dismissDelay (overrides singleton value)</li>
49197  * <li>target (required)</li>
49198  * <li>text (required)</li>
49199  * <li>title</li>
49200  * <li>width</li></ul></div>
49201  * <p>Here is an example showing how some of these config options could be used:</p>
49202  * <pre><code>
49203 // Init the singleton.  Any tag-based quick tips will start working.
49204 Ext.QuickTips.init();
49205
49206 // Apply a set of config properties to the singleton
49207 Ext.apply(Ext.QuickTips.getQuickTip(), {
49208     maxWidth: 200,
49209     minWidth: 100,
49210     showDelay: 50,
49211     trackMouse: true
49212 });
49213
49214 // Manually register a quick tip for a specific element
49215 Ext.QuickTips.register({
49216     target: 'my-div',
49217     title: 'My Tooltip',
49218     text: 'This tooltip was added in code',
49219     width: 100,
49220     dismissDelay: 20
49221 });
49222 </code></pre>
49223  * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with
49224  * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary
49225  * of supported attributes (optional unless otherwise noted):</p>
49226  * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the
49227  * same as autoHide = true.</li>
49228  * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>
49229  * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>
49230  * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>
49231  * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>
49232  * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>
49233  * <pre><code>
49234 // Add a quick tip to an HTML button
49235 &lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"
49236      ext:qtip="This is a quick tip from markup!">&lt;/input>
49237 </code></pre>
49238  * @singleton
49239  */
49240 Ext.QuickTips = function(){
49241     var tip, locks = [];
49242     return {
49243         /**
49244          * Initialize the global QuickTips instance and prepare any quick tips.
49245          * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) 
49246          */
49247         init : function(autoRender){
49248             if(!tip){
49249                 if(!Ext.isReady){
49250                     Ext.onReady(function(){
49251                         Ext.QuickTips.init(autoRender);
49252                     });
49253                     return;
49254                 }
49255                 tip = new Ext.QuickTip({elements:'header,body'});
49256                 if(autoRender !== false){
49257                     tip.render(Ext.getBody());
49258                 }
49259             }
49260         },
49261
49262         /**
49263          * Enable quick tips globally.
49264          */
49265         enable : function(){
49266             if(tip){
49267                 locks.pop();
49268                 if(locks.length < 1){
49269                     tip.enable();
49270                 }
49271             }
49272         },
49273
49274         /**
49275          * Disable quick tips globally.
49276          */
49277         disable : function(){
49278             if(tip){
49279                 tip.disable();
49280             }
49281             locks.push(1);
49282         },
49283
49284         /**
49285          * Returns true if quick tips are enabled, else false.
49286          * @return {Boolean}
49287          */
49288         isEnabled : function(){
49289             return tip !== undefined && !tip.disabled;
49290         },
49291
49292         /**
49293          * Gets the global QuickTips instance.
49294          */
49295         getQuickTip : function(){
49296             return tip;
49297         },
49298
49299         /**
49300          * Configures a new quick tip instance and assigns it to a target element.  See
49301          * {@link Ext.QuickTip#register} for details.
49302          * @param {Object} config The config object
49303          */
49304         register : function(){
49305             tip.register.apply(tip, arguments);
49306         },
49307
49308         /**
49309          * Removes any registered quick tip from the target element and destroys it.
49310          * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
49311          */
49312         unregister : function(){
49313             tip.unregister.apply(tip, arguments);
49314         },
49315
49316         /**
49317          * Alias of {@link #register}.
49318          * @param {Object} config The config object
49319          */
49320         tips :function(){
49321             tip.register.apply(tip, arguments);
49322         }
49323     }
49324 }();/**
49325  * @class Ext.slider.Tip
49326  * @extends Ext.Tip
49327  * Simple plugin for using an Ext.Tip with a slider to show the slider value. Example usage:
49328 <pre>
49329 new Ext.Slider({
49330     width: 214,
49331     minValue: 0,
49332     maxValue: 100,
49333     plugins: new Ext.slider.Tip()
49334 });
49335 </pre>
49336  * Optionally provide your own tip text by overriding getText:
49337  <pre>
49338  new Ext.Slider({
49339      width: 214,
49340      minValue: 0,
49341      maxValue: 100,
49342      plugins: new Ext.slider.Tip({
49343          getText: function(thumb){
49344              return String.format('<b>{0}% complete</b>', thumb.value);
49345          }
49346      })
49347  });
49348  </pre>
49349  */
49350 Ext.slider.Tip = Ext.extend(Ext.Tip, {
49351     minWidth: 10,
49352     offsets : [0, -10],
49353     
49354     init: function(slider) {
49355         slider.on({
49356             scope    : this,
49357             dragstart: this.onSlide,
49358             drag     : this.onSlide,
49359             dragend  : this.hide,
49360             destroy  : this.destroy
49361         });
49362     },
49363     
49364     /**
49365      * @private
49366      * Called whenever a dragstart or drag event is received on the associated Thumb. 
49367      * Aligns the Tip with the Thumb's new position.
49368      * @param {Ext.slider.MultiSlider} slider The slider
49369      * @param {Ext.EventObject} e The Event object
49370      * @param {Ext.slider.Thumb} thumb The thumb that the Tip is attached to
49371      */
49372     onSlide : function(slider, e, thumb) {
49373         this.show();
49374         this.body.update(this.getText(thumb));
49375         this.doAutoWidth();
49376         this.el.alignTo(thumb.el, 'b-t?', this.offsets);
49377     },
49378
49379     /**
49380      * Used to create the text that appears in the Tip's body. By default this just returns
49381      * the value of the Slider Thumb that the Tip is attached to. Override to customize.
49382      * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to
49383      * @return {String} The text to display in the tip
49384      */
49385     getText : function(thumb) {
49386         return String(thumb.value);
49387     }
49388 });
49389
49390 //backwards compatibility - SliderTip used to be a ux before 3.2
49391 Ext.ux.SliderTip = Ext.slider.Tip;/**
49392  * @class Ext.tree.TreePanel
49393  * @extends Ext.Panel
49394  * <p>The TreePanel provides tree-structured UI representation of tree-structured data.</p>
49395  * <p>{@link Ext.tree.TreeNode TreeNode}s added to the TreePanel may each contain metadata
49396  * used by your application in their {@link Ext.tree.TreeNode#attributes attributes} property.</p>
49397  * <p><b>A TreePanel must have a {@link #root} node before it is rendered.</b> This may either be
49398  * specified using the {@link #root} config option, or using the {@link #setRootNode} method.
49399  * <p>An example of tree rendered to an existing div:</p><pre><code>
49400 var tree = new Ext.tree.TreePanel({
49401     renderTo: 'tree-div',
49402     useArrows: true,
49403     autoScroll: true,
49404     animate: true,
49405     enableDD: true,
49406     containerScroll: true,
49407     border: false,
49408     // auto create TreeLoader
49409     dataUrl: 'get-nodes.php',
49410
49411     root: {
49412         nodeType: 'async',
49413         text: 'Ext JS',
49414         draggable: false,
49415         id: 'source'
49416     }
49417 });
49418
49419 tree.getRootNode().expand();
49420  * </code></pre>
49421  * <p>The example above would work with a data packet similar to this:</p><pre><code>
49422 [{
49423     "text": "adapter",
49424     "id": "source\/adapter",
49425     "cls": "folder"
49426 }, {
49427     "text": "dd",
49428     "id": "source\/dd",
49429     "cls": "folder"
49430 }, {
49431     "text": "debug.js",
49432     "id": "source\/debug.js",
49433     "leaf": true,
49434     "cls": "file"
49435 }]
49436  * </code></pre>
49437  * <p>An example of tree within a Viewport:</p><pre><code>
49438 new Ext.Viewport({
49439     layout: 'border',
49440     items: [{
49441         region: 'west',
49442         collapsible: true,
49443         title: 'Navigation',
49444         xtype: 'treepanel',
49445         width: 200,
49446         autoScroll: true,
49447         split: true,
49448         loader: new Ext.tree.TreeLoader(),
49449         root: new Ext.tree.AsyncTreeNode({
49450             expanded: true,
49451             children: [{
49452                 text: 'Menu Option 1',
49453                 leaf: true
49454             }, {
49455                 text: 'Menu Option 2',
49456                 leaf: true
49457             }, {
49458                 text: 'Menu Option 3',
49459                 leaf: true
49460             }]
49461         }),
49462         rootVisible: false,
49463         listeners: {
49464             click: function(n) {
49465                 Ext.Msg.alert('Navigation Tree Click', 'You clicked: "' + n.attributes.text + '"');
49466             }
49467         }
49468     }, {
49469         region: 'center',
49470         xtype: 'tabpanel',
49471         // remaining code not shown ...
49472     }]
49473 });
49474 </code></pre>
49475  *
49476  * @cfg {Ext.tree.TreeNode} root The root node for the tree.
49477  * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)
49478  * @cfg {Boolean} lines <tt>false</tt> to disable tree lines (defaults to <tt>true</tt>)
49479  * @cfg {Boolean} enableDD <tt>true</tt> to enable drag and drop
49480  * @cfg {Boolean} enableDrag <tt>true</tt> to enable just drag
49481  * @cfg {Boolean} enableDrop <tt>true</tt> to enable just drop
49482  * @cfg {Object} dragConfig Custom config to pass to the {@link Ext.tree.TreeDragZone} instance
49483  * @cfg {Object} dropConfig Custom config to pass to the {@link Ext.tree.TreeDropZone} instance
49484  * @cfg {String} ddGroup The DD group this TreePanel belongs to
49485  * @cfg {Boolean} ddAppendOnly <tt>true</tt> if the tree should only allow append drops (use for trees which are sorted)
49486  * @cfg {Boolean} ddScroll <tt>true</tt> to enable body scrolling
49487  * @cfg {Boolean} containerScroll <tt>true</tt> to register this container with ScrollManager
49488  * @cfg {Boolean} hlDrop <tt>false</tt> to disable node highlight on drop (defaults to the value of {@link Ext#enableFx})
49489  * @cfg {String} hlColor The color of the node highlight (defaults to <tt>'C3DAF9'</tt>)
49490  * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx})
49491  * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded
49492  * @cfg {Object} selModel A tree selection model to use with this TreePanel (defaults to an {@link Ext.tree.DefaultSelectionModel})
49493  * @cfg {Boolean} trackMouseOver <tt>false</tt> to disable mouse over highlighting
49494  * @cfg {Ext.tree.TreeLoader} loader A {@link Ext.tree.TreeLoader} for use with this TreePanel
49495  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to <tt>'/'</tt>)
49496  * @cfg {Boolean} useArrows <tt>true</tt> to use Vista-style arrows in the tree (defaults to <tt>false</tt>)
49497  * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).
49498  *
49499  * @constructor
49500  * @param {Object} config
49501  * @xtype treepanel
49502  */
49503 Ext.tree.TreePanel = Ext.extend(Ext.Panel, {
49504     rootVisible : true,
49505     animate : Ext.enableFx,
49506     lines : true,
49507     enableDD : false,
49508     hlDrop : Ext.enableFx,
49509     pathSeparator : '/',
49510
49511     /**
49512      * @cfg {Array} bubbleEvents
49513      * <p>An array of events that, when fired, should be bubbled to any parent container.
49514      * See {@link Ext.util.Observable#enableBubble}.
49515      * Defaults to <tt>[]</tt>.
49516      */
49517     bubbleEvents : [],
49518
49519     initComponent : function(){
49520         Ext.tree.TreePanel.superclass.initComponent.call(this);
49521
49522         if(!this.eventModel){
49523             this.eventModel = new Ext.tree.TreeEventModel(this);
49524         }
49525
49526         // initialize the loader
49527         var l = this.loader;
49528         if(!l){
49529             l = new Ext.tree.TreeLoader({
49530                 dataUrl: this.dataUrl,
49531                 requestMethod: this.requestMethod
49532             });
49533         }else if(Ext.isObject(l) && !l.load){
49534             l = new Ext.tree.TreeLoader(l);
49535         }
49536         this.loader = l;
49537
49538         this.nodeHash = {};
49539
49540         /**
49541         * The root node of this tree.
49542         * @type Ext.tree.TreeNode
49543         * @property root
49544         */
49545         if(this.root){
49546             var r = this.root;
49547             delete this.root;
49548             this.setRootNode(r);
49549         }
49550
49551
49552         this.addEvents(
49553
49554             /**
49555             * @event append
49556             * Fires when a new child node is appended to a node in this tree.
49557             * @param {Tree} tree The owner tree
49558             * @param {Node} parent The parent node
49559             * @param {Node} node The newly appended node
49560             * @param {Number} index The index of the newly appended node
49561             */
49562            'append',
49563            /**
49564             * @event remove
49565             * Fires when a child node is removed from a node in this tree.
49566             * @param {Tree} tree The owner tree
49567             * @param {Node} parent The parent node
49568             * @param {Node} node The child node removed
49569             */
49570            'remove',
49571            /**
49572             * @event movenode
49573             * Fires when a node is moved to a new location in the tree
49574             * @param {Tree} tree The owner tree
49575             * @param {Node} node The node moved
49576             * @param {Node} oldParent The old parent of this node
49577             * @param {Node} newParent The new parent of this node
49578             * @param {Number} index The index it was moved to
49579             */
49580            'movenode',
49581            /**
49582             * @event insert
49583             * Fires when a new child node is inserted in a node in this tree.
49584             * @param {Tree} tree The owner tree
49585             * @param {Node} parent The parent node
49586             * @param {Node} node The child node inserted
49587             * @param {Node} refNode The child node the node was inserted before
49588             */
49589            'insert',
49590            /**
49591             * @event beforeappend
49592             * Fires before a new child is appended to a node in this tree, return false to cancel the append.
49593             * @param {Tree} tree The owner tree
49594             * @param {Node} parent The parent node
49595             * @param {Node} node The child node to be appended
49596             */
49597            'beforeappend',
49598            /**
49599             * @event beforeremove
49600             * Fires before a child is removed from a node in this tree, return false to cancel the remove.
49601             * @param {Tree} tree The owner tree
49602             * @param {Node} parent The parent node
49603             * @param {Node} node The child node to be removed
49604             */
49605            'beforeremove',
49606            /**
49607             * @event beforemovenode
49608             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
49609             * @param {Tree} tree The owner tree
49610             * @param {Node} node The node being moved
49611             * @param {Node} oldParent The parent of the node
49612             * @param {Node} newParent The new parent the node is moving to
49613             * @param {Number} index The index it is being moved to
49614             */
49615            'beforemovenode',
49616            /**
49617             * @event beforeinsert
49618             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
49619             * @param {Tree} tree The owner tree
49620             * @param {Node} parent The parent node
49621             * @param {Node} node The child node to be inserted
49622             * @param {Node} refNode The child node the node is being inserted before
49623             */
49624             'beforeinsert',
49625
49626             /**
49627             * @event beforeload
49628             * Fires before a node is loaded, return false to cancel
49629             * @param {Node} node The node being loaded
49630             */
49631             'beforeload',
49632             /**
49633             * @event load
49634             * Fires when a node is loaded
49635             * @param {Node} node The node that was loaded
49636             */
49637             'load',
49638             /**
49639             * @event textchange
49640             * Fires when the text for a node is changed
49641             * @param {Node} node The node
49642             * @param {String} text The new text
49643             * @param {String} oldText The old text
49644             */
49645             'textchange',
49646             /**
49647             * @event beforeexpandnode
49648             * Fires before a node is expanded, return false to cancel.
49649             * @param {Node} node The node
49650             * @param {Boolean} deep
49651             * @param {Boolean} anim
49652             */
49653             'beforeexpandnode',
49654             /**
49655             * @event beforecollapsenode
49656             * Fires before a node is collapsed, return false to cancel.
49657             * @param {Node} node The node
49658             * @param {Boolean} deep
49659             * @param {Boolean} anim
49660             */
49661             'beforecollapsenode',
49662             /**
49663             * @event expandnode
49664             * Fires when a node is expanded
49665             * @param {Node} node The node
49666             */
49667             'expandnode',
49668             /**
49669             * @event disabledchange
49670             * Fires when the disabled status of a node changes
49671             * @param {Node} node The node
49672             * @param {Boolean} disabled
49673             */
49674             'disabledchange',
49675             /**
49676             * @event collapsenode
49677             * Fires when a node is collapsed
49678             * @param {Node} node The node
49679             */
49680             'collapsenode',
49681             /**
49682             * @event beforeclick
49683             * Fires before click processing on a node. Return false to cancel the default action.
49684             * @param {Node} node The node
49685             * @param {Ext.EventObject} e The event object
49686             */
49687             'beforeclick',
49688             /**
49689             * @event click
49690             * Fires when a node is clicked
49691             * @param {Node} node The node
49692             * @param {Ext.EventObject} e The event object
49693             */
49694             'click',
49695             /**
49696             * @event containerclick
49697             * Fires when the tree container is clicked
49698             * @param {Tree} this
49699             * @param {Ext.EventObject} e The event object
49700             */
49701             'containerclick',
49702             /**
49703             * @event checkchange
49704             * Fires when a node with a checkbox's checked property changes
49705             * @param {Node} this This node
49706             * @param {Boolean} checked
49707             */
49708             'checkchange',
49709             /**
49710             * @event beforedblclick
49711             * Fires before double click processing on a node. Return false to cancel the default action.
49712             * @param {Node} node The node
49713             * @param {Ext.EventObject} e The event object
49714             */
49715             'beforedblclick',
49716             /**
49717             * @event dblclick
49718             * Fires when a node is double clicked
49719             * @param {Node} node The node
49720             * @param {Ext.EventObject} e The event object
49721             */
49722             'dblclick',
49723             /**
49724             * @event containerdblclick
49725             * Fires when the tree container is double clicked
49726             * @param {Tree} this
49727             * @param {Ext.EventObject} e The event object
49728             */
49729             'containerdblclick',
49730             /**
49731             * @event contextmenu
49732             * Fires when a node is right clicked. To display a context menu in response to this
49733             * event, first create a Menu object (see {@link Ext.menu.Menu} for details), then add
49734             * a handler for this event:<pre><code>
49735 new Ext.tree.TreePanel({
49736     title: 'My TreePanel',
49737     root: new Ext.tree.AsyncTreeNode({
49738         text: 'The Root',
49739         children: [
49740             { text: 'Child node 1', leaf: true },
49741             { text: 'Child node 2', leaf: true }
49742         ]
49743     }),
49744     contextMenu: new Ext.menu.Menu({
49745         items: [{
49746             id: 'delete-node',
49747             text: 'Delete Node'
49748         }],
49749         listeners: {
49750             itemclick: function(item) {
49751                 switch (item.id) {
49752                     case 'delete-node':
49753                         var n = item.parentMenu.contextNode;
49754                         if (n.parentNode) {
49755                             n.remove();
49756                         }
49757                         break;
49758                 }
49759             }
49760         }
49761     }),
49762     listeners: {
49763         contextmenu: function(node, e) {
49764 //          Register the context node with the menu so that a Menu Item's handler function can access
49765 //          it via its {@link Ext.menu.BaseItem#parentMenu parentMenu} property.
49766             node.select();
49767             var c = node.getOwnerTree().contextMenu;
49768             c.contextNode = node;
49769             c.showAt(e.getXY());
49770         }
49771     }
49772 });
49773 </code></pre>
49774             * @param {Node} node The node
49775             * @param {Ext.EventObject} e The event object
49776             */
49777             'contextmenu',
49778             /**
49779             * @event containercontextmenu
49780             * Fires when the tree container is right clicked
49781             * @param {Tree} this
49782             * @param {Ext.EventObject} e The event object
49783             */
49784             'containercontextmenu',
49785             /**
49786             * @event beforechildrenrendered
49787             * Fires right before the child nodes for a node are rendered
49788             * @param {Node} node The node
49789             */
49790             'beforechildrenrendered',
49791            /**
49792              * @event startdrag
49793              * Fires when a node starts being dragged
49794              * @param {Ext.tree.TreePanel} this
49795              * @param {Ext.tree.TreeNode} node
49796              * @param {event} e The raw browser event
49797              */
49798             'startdrag',
49799             /**
49800              * @event enddrag
49801              * Fires when a drag operation is complete
49802              * @param {Ext.tree.TreePanel} this
49803              * @param {Ext.tree.TreeNode} node
49804              * @param {event} e The raw browser event
49805              */
49806             'enddrag',
49807             /**
49808              * @event dragdrop
49809              * Fires when a dragged node is dropped on a valid DD target
49810              * @param {Ext.tree.TreePanel} this
49811              * @param {Ext.tree.TreeNode} node
49812              * @param {DD} dd The dd it was dropped on
49813              * @param {event} e The raw browser event
49814              */
49815             'dragdrop',
49816             /**
49817              * @event beforenodedrop
49818              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
49819              * passed to handlers has the following properties:<br />
49820              * <ul style="padding:5px;padding-left:16px;">
49821              * <li>tree - The TreePanel</li>
49822              * <li>target - The node being targeted for the drop</li>
49823              * <li>data - The drag data from the drag source</li>
49824              * <li>point - The point of the drop - append, above or below</li>
49825              * <li>source - The drag source</li>
49826              * <li>rawEvent - Raw mouse event</li>
49827              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
49828              * to be inserted by setting them on this object.</li>
49829              * <li>cancel - Set this to true to cancel the drop.</li>
49830              * <li>dropStatus - If the default drop action is cancelled but the drop is valid, setting this to true
49831              * will prevent the animated 'repair' from appearing.</li>
49832              * </ul>
49833              * @param {Object} dropEvent
49834              */
49835             'beforenodedrop',
49836             /**
49837              * @event nodedrop
49838              * Fires after a DD object is dropped on a node in this tree. The dropEvent
49839              * passed to handlers has the following properties:<br />
49840              * <ul style="padding:5px;padding-left:16px;">
49841              * <li>tree - The TreePanel</li>
49842              * <li>target - The node being targeted for the drop</li>
49843              * <li>data - The drag data from the drag source</li>
49844              * <li>point - The point of the drop - append, above or below</li>
49845              * <li>source - The drag source</li>
49846              * <li>rawEvent - Raw mouse event</li>
49847              * <li>dropNode - Dropped node(s).</li>
49848              * </ul>
49849              * @param {Object} dropEvent
49850              */
49851             'nodedrop',
49852              /**
49853              * @event nodedragover
49854              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
49855              * passed to handlers has the following properties:<br />
49856              * <ul style="padding:5px;padding-left:16px;">
49857              * <li>tree - The TreePanel</li>
49858              * <li>target - The node being targeted for the drop</li>
49859              * <li>data - The drag data from the drag source</li>
49860              * <li>point - The point of the drop - append, above or below</li>
49861              * <li>source - The drag source</li>
49862              * <li>rawEvent - Raw mouse event</li>
49863              * <li>dropNode - Drop node(s) provided by the source.</li>
49864              * <li>cancel - Set this to true to signal drop not allowed.</li>
49865              * </ul>
49866              * @param {Object} dragOverEvent
49867              */
49868             'nodedragover'
49869         );
49870         if(this.singleExpand){
49871             this.on('beforeexpandnode', this.restrictExpand, this);
49872         }
49873     },
49874
49875     // private
49876     proxyNodeEvent : function(ename, a1, a2, a3, a4, a5, a6){
49877         if(ename == 'collapse' || ename == 'expand' || ename == 'beforecollapse' || ename == 'beforeexpand' || ename == 'move' || ename == 'beforemove'){
49878             ename = ename+'node';
49879         }
49880         // args inline for performance while bubbling events
49881         return this.fireEvent(ename, a1, a2, a3, a4, a5, a6);
49882     },
49883
49884
49885     /**
49886      * Returns this root node for this tree
49887      * @return {Node}
49888      */
49889     getRootNode : function(){
49890         return this.root;
49891     },
49892
49893     /**
49894      * Sets the root node for this tree. If the TreePanel has already rendered a root node, the
49895      * previous root node (and all of its descendants) are destroyed before the new root node is rendered.
49896      * @param {Node} node
49897      * @return {Node}
49898      */
49899     setRootNode : function(node){
49900         this.destroyRoot();
49901         if(!node.render){ // attributes passed
49902             node = this.loader.createNode(node);
49903         }
49904         this.root = node;
49905         node.ownerTree = this;
49906         node.isRoot = true;
49907         this.registerNode(node);
49908         if(!this.rootVisible){
49909             var uiP = node.attributes.uiProvider;
49910             node.ui = uiP ? new uiP(node) : new Ext.tree.RootTreeNodeUI(node);
49911         }
49912         if(this.innerCt){
49913             this.clearInnerCt();
49914             this.renderRoot();
49915         }
49916         return node;
49917     },
49918     
49919     clearInnerCt : function(){
49920         this.innerCt.update('');    
49921     },
49922     
49923     // private
49924     renderRoot : function(){
49925         this.root.render();
49926         if(!this.rootVisible){
49927             this.root.renderChildren();
49928         }
49929     },
49930
49931     /**
49932      * Gets a node in this tree by its id
49933      * @param {String} id
49934      * @return {Node}
49935      */
49936     getNodeById : function(id){
49937         return this.nodeHash[id];
49938     },
49939
49940     // private
49941     registerNode : function(node){
49942         this.nodeHash[node.id] = node;
49943     },
49944
49945     // private
49946     unregisterNode : function(node){
49947         delete this.nodeHash[node.id];
49948     },
49949
49950     // private
49951     toString : function(){
49952         return '[Tree'+(this.id?' '+this.id:'')+']';
49953     },
49954
49955     // private
49956     restrictExpand : function(node){
49957         var p = node.parentNode;
49958         if(p){
49959             if(p.expandedChild && p.expandedChild.parentNode == p){
49960                 p.expandedChild.collapse();
49961             }
49962             p.expandedChild = node;
49963         }
49964     },
49965
49966     /**
49967      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. 'id')
49968      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
49969      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
49970      * @return {Array}
49971      */
49972     getChecked : function(a, startNode){
49973         startNode = startNode || this.root;
49974         var r = [];
49975         var f = function(){
49976             if(this.attributes.checked){
49977                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
49978             }
49979         };
49980         startNode.cascade(f);
49981         return r;
49982     },
49983
49984     /**
49985      * Returns the default {@link Ext.tree.TreeLoader} for this TreePanel.
49986      * @return {Ext.tree.TreeLoader} The TreeLoader for this TreePanel.
49987      */
49988     getLoader : function(){
49989         return this.loader;
49990     },
49991
49992     /**
49993      * Expand all nodes
49994      */
49995     expandAll : function(){
49996         this.root.expand(true);
49997     },
49998
49999     /**
50000      * Collapse all nodes
50001      */
50002     collapseAll : function(){
50003         this.root.collapse(true);
50004     },
50005
50006     /**
50007      * Returns the selection model used by this TreePanel.
50008      * @return {TreeSelectionModel} The selection model used by this TreePanel
50009      */
50010     getSelectionModel : function(){
50011         if(!this.selModel){
50012             this.selModel = new Ext.tree.DefaultSelectionModel();
50013         }
50014         return this.selModel;
50015     },
50016
50017     /**
50018      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
50019      * @param {String} path
50020      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
50021      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
50022      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
50023      */
50024     expandPath : function(path, attr, callback){
50025         attr = attr || 'id';
50026         var keys = path.split(this.pathSeparator);
50027         var curNode = this.root;
50028         if(curNode.attributes[attr] != keys[1]){ // invalid root
50029             if(callback){
50030                 callback(false, null);
50031             }
50032             return;
50033         }
50034         var index = 1;
50035         var f = function(){
50036             if(++index == keys.length){
50037                 if(callback){
50038                     callback(true, curNode);
50039                 }
50040                 return;
50041             }
50042             var c = curNode.findChild(attr, keys[index]);
50043             if(!c){
50044                 if(callback){
50045                     callback(false, curNode);
50046                 }
50047                 return;
50048             }
50049             curNode = c;
50050             c.expand(false, false, f);
50051         };
50052         curNode.expand(false, false, f);
50053     },
50054
50055     /**
50056      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Ext.data.Node#getPath}
50057      * @param {String} path
50058      * @param {String} attr (optional) The attribute used in the path (see {@link Ext.data.Node#getPath} for more info)
50059      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
50060      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
50061      */
50062     selectPath : function(path, attr, callback){
50063         attr = attr || 'id';
50064         var keys = path.split(this.pathSeparator),
50065             v = keys.pop();
50066         if(keys.length > 1){
50067             var f = function(success, node){
50068                 if(success && node){
50069                     var n = node.findChild(attr, v);
50070                     if(n){
50071                         n.select();
50072                         if(callback){
50073                             callback(true, n);
50074                         }
50075                     }else if(callback){
50076                         callback(false, n);
50077                     }
50078                 }else{
50079                     if(callback){
50080                         callback(false, n);
50081                     }
50082                 }
50083             };
50084             this.expandPath(keys.join(this.pathSeparator), attr, f);
50085         }else{
50086             this.root.select();
50087             if(callback){
50088                 callback(true, this.root);
50089             }
50090         }
50091     },
50092
50093     /**
50094      * Returns the underlying Element for this tree
50095      * @return {Ext.Element} The Element
50096      */
50097     getTreeEl : function(){
50098         return this.body;
50099     },
50100
50101     // private
50102     onRender : function(ct, position){
50103         Ext.tree.TreePanel.superclass.onRender.call(this, ct, position);
50104         this.el.addClass('x-tree');
50105         this.innerCt = this.body.createChild({tag:'ul',
50106                cls:'x-tree-root-ct ' +
50107                (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')});
50108     },
50109
50110     // private
50111     initEvents : function(){
50112         Ext.tree.TreePanel.superclass.initEvents.call(this);
50113
50114         if(this.containerScroll){
50115             Ext.dd.ScrollManager.register(this.body);
50116         }
50117         if((this.enableDD || this.enableDrop) && !this.dropZone){
50118            /**
50119             * The dropZone used by this tree if drop is enabled (see {@link #enableDD} or {@link #enableDrop})
50120             * @property dropZone
50121             * @type Ext.tree.TreeDropZone
50122             */
50123              this.dropZone = new Ext.tree.TreeDropZone(this, this.dropConfig || {
50124                ddGroup: this.ddGroup || 'TreeDD', appendOnly: this.ddAppendOnly === true
50125            });
50126         }
50127         if((this.enableDD || this.enableDrag) && !this.dragZone){
50128            /**
50129             * The dragZone used by this tree if drag is enabled (see {@link #enableDD} or {@link #enableDrag})
50130             * @property dragZone
50131             * @type Ext.tree.TreeDragZone
50132             */
50133             this.dragZone = new Ext.tree.TreeDragZone(this, this.dragConfig || {
50134                ddGroup: this.ddGroup || 'TreeDD',
50135                scroll: this.ddScroll
50136            });
50137         }
50138         this.getSelectionModel().init(this);
50139     },
50140
50141     // private
50142     afterRender : function(){
50143         Ext.tree.TreePanel.superclass.afterRender.call(this);
50144         this.renderRoot();
50145     },
50146
50147     beforeDestroy : function(){
50148         if(this.rendered){
50149             Ext.dd.ScrollManager.unregister(this.body);
50150             Ext.destroy(this.dropZone, this.dragZone);
50151         }
50152         this.destroyRoot();
50153         Ext.destroy(this.loader);
50154         this.nodeHash = this.root = this.loader = null;
50155         Ext.tree.TreePanel.superclass.beforeDestroy.call(this);
50156     },
50157     
50158     /**
50159      * Destroy the root node. Not included by itself because we need to pass the silent parameter.
50160      * @private
50161      */
50162     destroyRoot : function(){
50163         if(this.root && this.root.destroy){
50164             this.root.destroy(true);
50165         }
50166     }
50167
50168     /**
50169      * @cfg {String/Number} activeItem
50170      * @hide
50171      */
50172     /**
50173      * @cfg {Boolean} autoDestroy
50174      * @hide
50175      */
50176     /**
50177      * @cfg {Object/String/Function} autoLoad
50178      * @hide
50179      */
50180     /**
50181      * @cfg {Boolean} autoWidth
50182      * @hide
50183      */
50184     /**
50185      * @cfg {Boolean/Number} bufferResize
50186      * @hide
50187      */
50188     /**
50189      * @cfg {String} defaultType
50190      * @hide
50191      */
50192     /**
50193      * @cfg {Object} defaults
50194      * @hide
50195      */
50196     /**
50197      * @cfg {Boolean} hideBorders
50198      * @hide
50199      */
50200     /**
50201      * @cfg {Mixed} items
50202      * @hide
50203      */
50204     /**
50205      * @cfg {String} layout
50206      * @hide
50207      */
50208     /**
50209      * @cfg {Object} layoutConfig
50210      * @hide
50211      */
50212     /**
50213      * @cfg {Boolean} monitorResize
50214      * @hide
50215      */
50216     /**
50217      * @property items
50218      * @hide
50219      */
50220     /**
50221      * @method cascade
50222      * @hide
50223      */
50224     /**
50225      * @method doLayout
50226      * @hide
50227      */
50228     /**
50229      * @method find
50230      * @hide
50231      */
50232     /**
50233      * @method findBy
50234      * @hide
50235      */
50236     /**
50237      * @method findById
50238      * @hide
50239      */
50240     /**
50241      * @method findByType
50242      * @hide
50243      */
50244     /**
50245      * @method getComponent
50246      * @hide
50247      */
50248     /**
50249      * @method getLayout
50250      * @hide
50251      */
50252     /**
50253      * @method getUpdater
50254      * @hide
50255      */
50256     /**
50257      * @method insert
50258      * @hide
50259      */
50260     /**
50261      * @method load
50262      * @hide
50263      */
50264     /**
50265      * @method remove
50266      * @hide
50267      */
50268     /**
50269      * @event add
50270      * @hide
50271      */
50272     /**
50273      * @method removeAll
50274      * @hide
50275      */
50276     /**
50277      * @event afterLayout
50278      * @hide
50279      */
50280     /**
50281      * @event beforeadd
50282      * @hide
50283      */
50284     /**
50285      * @event beforeremove
50286      * @hide
50287      */
50288     /**
50289      * @event remove
50290      * @hide
50291      */
50292
50293
50294
50295     /**
50296      * @cfg {String} allowDomMove  @hide
50297      */
50298     /**
50299      * @cfg {String} autoEl @hide
50300      */
50301     /**
50302      * @cfg {String} applyTo  @hide
50303      */
50304     /**
50305      * @cfg {String} contentEl  @hide
50306      */
50307     /**
50308      * @cfg {Mixed} data  @hide
50309      */
50310     /**
50311      * @cfg {Mixed} tpl  @hide
50312      */
50313     /**
50314      * @cfg {String} tplWriteMode  @hide
50315      */
50316     /**
50317      * @cfg {String} disabledClass  @hide
50318      */
50319     /**
50320      * @cfg {String} elements  @hide
50321      */
50322     /**
50323      * @cfg {String} html  @hide
50324      */
50325     /**
50326      * @cfg {Boolean} preventBodyReset
50327      * @hide
50328      */
50329     /**
50330      * @property disabled
50331      * @hide
50332      */
50333     /**
50334      * @method applyToMarkup
50335      * @hide
50336      */
50337     /**
50338      * @method enable
50339      * @hide
50340      */
50341     /**
50342      * @method disable
50343      * @hide
50344      */
50345     /**
50346      * @method setDisabled
50347      * @hide
50348      */
50349 });
50350
50351 Ext.tree.TreePanel.nodeTypes = {};
50352
50353 Ext.reg('treepanel', Ext.tree.TreePanel);Ext.tree.TreeEventModel = function(tree){
50354     this.tree = tree;
50355     this.tree.on('render', this.initEvents, this);
50356 };
50357
50358 Ext.tree.TreeEventModel.prototype = {
50359     initEvents : function(){
50360         var t = this.tree;
50361
50362         if(t.trackMouseOver !== false){
50363             t.mon(t.innerCt, {
50364                 scope: this,
50365                 mouseover: this.delegateOver,
50366                 mouseout: this.delegateOut
50367             });
50368         }
50369         t.mon(t.getTreeEl(), {
50370             scope: this,
50371             click: this.delegateClick,
50372             dblclick: this.delegateDblClick,
50373             contextmenu: this.delegateContextMenu
50374         });
50375     },
50376
50377     getNode : function(e){
50378         var t;
50379         if(t = e.getTarget('.x-tree-node-el', 10)){
50380             var id = Ext.fly(t, '_treeEvents').getAttribute('tree-node-id', 'ext');
50381             if(id){
50382                 return this.tree.getNodeById(id);
50383             }
50384         }
50385         return null;
50386     },
50387
50388     getNodeTarget : function(e){
50389         var t = e.getTarget('.x-tree-node-icon', 1);
50390         if(!t){
50391             t = e.getTarget('.x-tree-node-el', 6);
50392         }
50393         return t;
50394     },
50395
50396     delegateOut : function(e, t){
50397         if(!this.beforeEvent(e)){
50398             return;
50399         }
50400         if(e.getTarget('.x-tree-ec-icon', 1)){
50401             var n = this.getNode(e);
50402             this.onIconOut(e, n);
50403             if(n == this.lastEcOver){
50404                 delete this.lastEcOver;
50405             }
50406         }
50407         if((t = this.getNodeTarget(e)) && !e.within(t, true)){
50408             this.onNodeOut(e, this.getNode(e));
50409         }
50410     },
50411
50412     delegateOver : function(e, t){
50413         if(!this.beforeEvent(e)){
50414             return;
50415         }
50416         if(Ext.isGecko && !this.trackingDoc){ // prevent hanging in FF
50417             Ext.getBody().on('mouseover', this.trackExit, this);
50418             this.trackingDoc = true;
50419         }
50420         if(this.lastEcOver){ // prevent hung highlight
50421             this.onIconOut(e, this.lastEcOver);
50422             delete this.lastEcOver;
50423         }
50424         if(e.getTarget('.x-tree-ec-icon', 1)){
50425             this.lastEcOver = this.getNode(e);
50426             this.onIconOver(e, this.lastEcOver);
50427         }
50428         if(t = this.getNodeTarget(e)){
50429             this.onNodeOver(e, this.getNode(e));
50430         }
50431     },
50432
50433     trackExit : function(e){
50434         if(this.lastOverNode){
50435             if(this.lastOverNode.ui && !e.within(this.lastOverNode.ui.getEl())){
50436                 this.onNodeOut(e, this.lastOverNode);
50437             }
50438             delete this.lastOverNode;
50439             Ext.getBody().un('mouseover', this.trackExit, this);
50440             this.trackingDoc = false;
50441         }
50442
50443     },
50444
50445     delegateClick : function(e, t){
50446         if(this.beforeEvent(e)){
50447             if(e.getTarget('input[type=checkbox]', 1)){
50448                 this.onCheckboxClick(e, this.getNode(e));
50449             }else if(e.getTarget('.x-tree-ec-icon', 1)){
50450                 this.onIconClick(e, this.getNode(e));
50451             }else if(this.getNodeTarget(e)){
50452                 this.onNodeClick(e, this.getNode(e));
50453             }
50454         }else{
50455             this.checkContainerEvent(e, 'click');
50456         }
50457     },
50458
50459     delegateDblClick : function(e, t){
50460         if(this.beforeEvent(e)){
50461             if(this.getNodeTarget(e)){
50462                 this.onNodeDblClick(e, this.getNode(e));
50463             }
50464         }else{
50465             this.checkContainerEvent(e, 'dblclick');
50466         }
50467     },
50468
50469     delegateContextMenu : function(e, t){
50470         if(this.beforeEvent(e)){
50471             if(this.getNodeTarget(e)){
50472                 this.onNodeContextMenu(e, this.getNode(e));
50473             }
50474         }else{
50475             this.checkContainerEvent(e, 'contextmenu');
50476         }
50477     },
50478     
50479     checkContainerEvent: function(e, type){
50480         if(this.disabled){
50481             e.stopEvent();
50482             return false;
50483         }
50484         this.onContainerEvent(e, type);    
50485     },
50486
50487     onContainerEvent: function(e, type){
50488         this.tree.fireEvent('container' + type, this.tree, e);
50489     },
50490
50491     onNodeClick : function(e, node){
50492         node.ui.onClick(e);
50493     },
50494
50495     onNodeOver : function(e, node){
50496         this.lastOverNode = node;
50497         node.ui.onOver(e);
50498     },
50499
50500     onNodeOut : function(e, node){
50501         node.ui.onOut(e);
50502     },
50503
50504     onIconOver : function(e, node){
50505         node.ui.addClass('x-tree-ec-over');
50506     },
50507
50508     onIconOut : function(e, node){
50509         node.ui.removeClass('x-tree-ec-over');
50510     },
50511
50512     onIconClick : function(e, node){
50513         node.ui.ecClick(e);
50514     },
50515
50516     onCheckboxClick : function(e, node){
50517         node.ui.onCheckChange(e);
50518     },
50519
50520     onNodeDblClick : function(e, node){
50521         node.ui.onDblClick(e);
50522     },
50523
50524     onNodeContextMenu : function(e, node){
50525         node.ui.onContextMenu(e);
50526     },
50527
50528     beforeEvent : function(e){
50529         var node = this.getNode(e);
50530         if(this.disabled || !node || !node.ui){
50531             e.stopEvent();
50532             return false;
50533         }
50534         return true;
50535     },
50536
50537     disable: function(){
50538         this.disabled = true;
50539     },
50540
50541     enable: function(){
50542         this.disabled = false;
50543     }
50544 };/**
50545  * @class Ext.tree.DefaultSelectionModel
50546  * @extends Ext.util.Observable
50547  * The default single selection for a TreePanel.
50548  */
50549 Ext.tree.DefaultSelectionModel = Ext.extend(Ext.util.Observable, {
50550     
50551     constructor : function(config){
50552         this.selNode = null;
50553    
50554         this.addEvents(
50555             /**
50556              * @event selectionchange
50557              * Fires when the selected node changes
50558              * @param {DefaultSelectionModel} this
50559              * @param {TreeNode} node the new selection
50560              */
50561             'selectionchange',
50562
50563             /**
50564              * @event beforeselect
50565              * Fires before the selected node changes, return false to cancel the change
50566              * @param {DefaultSelectionModel} this
50567              * @param {TreeNode} node the new selection
50568              * @param {TreeNode} node the old selection
50569              */
50570             'beforeselect'
50571         );
50572
50573         Ext.apply(this, config);
50574         Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);    
50575     },
50576     
50577     init : function(tree){
50578         this.tree = tree;
50579         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
50580         tree.on('click', this.onNodeClick, this);
50581     },
50582     
50583     onNodeClick : function(node, e){
50584         this.select(node);
50585     },
50586     
50587     /**
50588      * Select a node.
50589      * @param {TreeNode} node The node to select
50590      * @return {TreeNode} The selected node
50591      */
50592     select : function(node, /* private*/ selectNextNode){
50593         // If node is hidden, select the next node in whatever direction was being moved in.
50594         if (!Ext.fly(node.ui.wrap).isVisible() && selectNextNode) {
50595             return selectNextNode.call(this, node);
50596         }
50597         var last = this.selNode;
50598         if(node == last){
50599             node.ui.onSelectedChange(true);
50600         }else if(this.fireEvent('beforeselect', this, node, last) !== false){
50601             if(last && last.ui){
50602                 last.ui.onSelectedChange(false);
50603             }
50604             this.selNode = node;
50605             node.ui.onSelectedChange(true);
50606             this.fireEvent('selectionchange', this, node, last);
50607         }
50608         return node;
50609     },
50610     
50611     /**
50612      * Deselect a node.
50613      * @param {TreeNode} node The node to unselect
50614      * @param {Boolean} silent True to stop the selectionchange event from firing.
50615      */
50616     unselect : function(node, silent){
50617         if(this.selNode == node){
50618             this.clearSelections(silent);
50619         }    
50620     },
50621     
50622     /**
50623      * Clear all selections
50624      * @param {Boolean} silent True to stop the selectionchange event from firing.
50625      */
50626     clearSelections : function(silent){
50627         var n = this.selNode;
50628         if(n){
50629             n.ui.onSelectedChange(false);
50630             this.selNode = null;
50631             if(silent !== true){
50632                 this.fireEvent('selectionchange', this, null);
50633             }
50634         }
50635         return n;
50636     },
50637     
50638     /**
50639      * Get the selected node
50640      * @return {TreeNode} The selected node
50641      */
50642     getSelectedNode : function(){
50643         return this.selNode;    
50644     },
50645     
50646     /**
50647      * Returns true if the node is selected
50648      * @param {TreeNode} node The node to check
50649      * @return {Boolean}
50650      */
50651     isSelected : function(node){
50652         return this.selNode == node;  
50653     },
50654
50655     /**
50656      * Selects the node above the selected node in the tree, intelligently walking the nodes
50657      * @return TreeNode The new selection
50658      */
50659     selectPrevious : function(/* private */ s){
50660         if(!(s = s || this.selNode || this.lastSelNode)){
50661             return null;
50662         }
50663         // Here we pass in the current function to select to indicate the direction we're moving
50664         var ps = s.previousSibling;
50665         if(ps){
50666             if(!ps.isExpanded() || ps.childNodes.length < 1){
50667                 return this.select(ps, this.selectPrevious);
50668             } else{
50669                 var lc = ps.lastChild;
50670                 while(lc && lc.isExpanded() && Ext.fly(lc.ui.wrap).isVisible() && lc.childNodes.length > 0){
50671                     lc = lc.lastChild;
50672                 }
50673                 return this.select(lc, this.selectPrevious);
50674             }
50675         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
50676             return this.select(s.parentNode, this.selectPrevious);
50677         }
50678         return null;
50679     },
50680
50681     /**
50682      * Selects the node above the selected node in the tree, intelligently walking the nodes
50683      * @return TreeNode The new selection
50684      */
50685     selectNext : function(/* private */ s){
50686         if(!(s = s || this.selNode || this.lastSelNode)){
50687             return null;
50688         }
50689         // Here we pass in the current function to select to indicate the direction we're moving
50690         if(s.firstChild && s.isExpanded() && Ext.fly(s.ui.wrap).isVisible()){
50691              return this.select(s.firstChild, this.selectNext);
50692          }else if(s.nextSibling){
50693              return this.select(s.nextSibling, this.selectNext);
50694          }else if(s.parentNode){
50695             var newS = null;
50696             s.parentNode.bubble(function(){
50697                 if(this.nextSibling){
50698                     newS = this.getOwnerTree().selModel.select(this.nextSibling, this.selectNext);
50699                     return false;
50700                 }
50701             });
50702             return newS;
50703          }
50704         return null;
50705     },
50706
50707     onKeyDown : function(e){
50708         var s = this.selNode || this.lastSelNode;
50709         // undesirable, but required
50710         var sm = this;
50711         if(!s){
50712             return;
50713         }
50714         var k = e.getKey();
50715         switch(k){
50716              case e.DOWN:
50717                  e.stopEvent();
50718                  this.selectNext();
50719              break;
50720              case e.UP:
50721                  e.stopEvent();
50722                  this.selectPrevious();
50723              break;
50724              case e.RIGHT:
50725                  e.preventDefault();
50726                  if(s.hasChildNodes()){
50727                      if(!s.isExpanded()){
50728                          s.expand();
50729                      }else if(s.firstChild){
50730                          this.select(s.firstChild, e);
50731                      }
50732                  }
50733              break;
50734              case e.LEFT:
50735                  e.preventDefault();
50736                  if(s.hasChildNodes() && s.isExpanded()){
50737                      s.collapse();
50738                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
50739                      this.select(s.parentNode, e);
50740                  }
50741              break;
50742         };
50743     }
50744 });
50745
50746 /**
50747  * @class Ext.tree.MultiSelectionModel
50748  * @extends Ext.util.Observable
50749  * Multi selection for a TreePanel.
50750  */
50751 Ext.tree.MultiSelectionModel = Ext.extend(Ext.util.Observable, {
50752     
50753     constructor : function(config){
50754         this.selNodes = [];
50755         this.selMap = {};
50756         this.addEvents(
50757             /**
50758              * @event selectionchange
50759              * Fires when the selected nodes change
50760              * @param {MultiSelectionModel} this
50761              * @param {Array} nodes Array of the selected nodes
50762              */
50763             'selectionchange'
50764         );
50765         Ext.apply(this, config);
50766         Ext.tree.MultiSelectionModel.superclass.constructor.call(this);    
50767     },
50768     
50769     init : function(tree){
50770         this.tree = tree;
50771         tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
50772         tree.on('click', this.onNodeClick, this);
50773     },
50774     
50775     onNodeClick : function(node, e){
50776         if(e.ctrlKey && this.isSelected(node)){
50777             this.unselect(node);
50778         }else{
50779             this.select(node, e, e.ctrlKey);
50780         }
50781     },
50782     
50783     /**
50784      * Select a node.
50785      * @param {TreeNode} node The node to select
50786      * @param {EventObject} e (optional) An event associated with the selection
50787      * @param {Boolean} keepExisting True to retain existing selections
50788      * @return {TreeNode} The selected node
50789      */
50790     select : function(node, e, keepExisting){
50791         if(keepExisting !== true){
50792             this.clearSelections(true);
50793         }
50794         if(this.isSelected(node)){
50795             this.lastSelNode = node;
50796             return node;
50797         }
50798         this.selNodes.push(node);
50799         this.selMap[node.id] = node;
50800         this.lastSelNode = node;
50801         node.ui.onSelectedChange(true);
50802         this.fireEvent('selectionchange', this, this.selNodes);
50803         return node;
50804     },
50805     
50806     /**
50807      * Deselect a node.
50808      * @param {TreeNode} node The node to unselect
50809      */
50810     unselect : function(node){
50811         if(this.selMap[node.id]){
50812             node.ui.onSelectedChange(false);
50813             var sn = this.selNodes;
50814             var index = sn.indexOf(node);
50815             if(index != -1){
50816                 this.selNodes.splice(index, 1);
50817             }
50818             delete this.selMap[node.id];
50819             this.fireEvent('selectionchange', this, this.selNodes);
50820         }
50821     },
50822     
50823     /**
50824      * Clear all selections
50825      */
50826     clearSelections : function(suppressEvent){
50827         var sn = this.selNodes;
50828         if(sn.length > 0){
50829             for(var i = 0, len = sn.length; i < len; i++){
50830                 sn[i].ui.onSelectedChange(false);
50831             }
50832             this.selNodes = [];
50833             this.selMap = {};
50834             if(suppressEvent !== true){
50835                 this.fireEvent('selectionchange', this, this.selNodes);
50836             }
50837         }
50838     },
50839     
50840     /**
50841      * Returns true if the node is selected
50842      * @param {TreeNode} node The node to check
50843      * @return {Boolean}
50844      */
50845     isSelected : function(node){
50846         return this.selMap[node.id] ? true : false;  
50847     },
50848     
50849     /**
50850      * Returns an array of the selected nodes
50851      * @return {Array}
50852      */
50853     getSelectedNodes : function(){
50854         return this.selNodes.concat([]);
50855     },
50856
50857     onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,
50858
50859     selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,
50860
50861     selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious
50862 });/**
50863  * @class Ext.data.Tree
50864  * @extends Ext.util.Observable
50865  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
50866  * in the tree have most standard DOM functionality.
50867  * @constructor
50868  * @param {Node} root (optional) The root node
50869  */
50870 Ext.data.Tree = function(root){
50871    this.nodeHash = {};
50872    /**
50873     * The root node for this tree
50874     * @type Node
50875     */
50876    this.root = null;
50877    if(root){
50878        this.setRootNode(root);
50879    }
50880    this.addEvents(
50881        /**
50882         * @event append
50883         * Fires when a new child node is appended to a node in this tree.
50884         * @param {Tree} tree The owner tree
50885         * @param {Node} parent The parent node
50886         * @param {Node} node The newly appended node
50887         * @param {Number} index The index of the newly appended node
50888         */
50889        "append",
50890        /**
50891         * @event remove
50892         * Fires when a child node is removed from a node in this tree.
50893         * @param {Tree} tree The owner tree
50894         * @param {Node} parent The parent node
50895         * @param {Node} node The child node removed
50896         */
50897        "remove",
50898        /**
50899         * @event move
50900         * Fires when a node is moved to a new location in the tree
50901         * @param {Tree} tree The owner tree
50902         * @param {Node} node The node moved
50903         * @param {Node} oldParent The old parent of this node
50904         * @param {Node} newParent The new parent of this node
50905         * @param {Number} index The index it was moved to
50906         */
50907        "move",
50908        /**
50909         * @event insert
50910         * Fires when a new child node is inserted in a node in this tree.
50911         * @param {Tree} tree The owner tree
50912         * @param {Node} parent The parent node
50913         * @param {Node} node The child node inserted
50914         * @param {Node} refNode The child node the node was inserted before
50915         */
50916        "insert",
50917        /**
50918         * @event beforeappend
50919         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
50920         * @param {Tree} tree The owner tree
50921         * @param {Node} parent The parent node
50922         * @param {Node} node The child node to be appended
50923         */
50924        "beforeappend",
50925        /**
50926         * @event beforeremove
50927         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
50928         * @param {Tree} tree The owner tree
50929         * @param {Node} parent The parent node
50930         * @param {Node} node The child node to be removed
50931         */
50932        "beforeremove",
50933        /**
50934         * @event beforemove
50935         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
50936         * @param {Tree} tree The owner tree
50937         * @param {Node} node The node being moved
50938         * @param {Node} oldParent The parent of the node
50939         * @param {Node} newParent The new parent the node is moving to
50940         * @param {Number} index The index it is being moved to
50941         */
50942        "beforemove",
50943        /**
50944         * @event beforeinsert
50945         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
50946         * @param {Tree} tree The owner tree
50947         * @param {Node} parent The parent node
50948         * @param {Node} node The child node to be inserted
50949         * @param {Node} refNode The child node the node is being inserted before
50950         */
50951        "beforeinsert"
50952    );
50953
50954     Ext.data.Tree.superclass.constructor.call(this);
50955 };
50956
50957 Ext.extend(Ext.data.Tree, Ext.util.Observable, {
50958     /**
50959      * @cfg {String} pathSeparator
50960      * The token used to separate paths in node ids (defaults to '/').
50961      */
50962     pathSeparator: "/",
50963
50964     // private
50965     proxyNodeEvent : function(){
50966         return this.fireEvent.apply(this, arguments);
50967     },
50968
50969     /**
50970      * Returns the root node for this tree.
50971      * @return {Node}
50972      */
50973     getRootNode : function(){
50974         return this.root;
50975     },
50976
50977     /**
50978      * Sets the root node for this tree.
50979      * @param {Node} node
50980      * @return {Node}
50981      */
50982     setRootNode : function(node){
50983         this.root = node;
50984         node.ownerTree = this;
50985         node.isRoot = true;
50986         this.registerNode(node);
50987         return node;
50988     },
50989
50990     /**
50991      * Gets a node in this tree by its id.
50992      * @param {String} id
50993      * @return {Node}
50994      */
50995     getNodeById : function(id){
50996         return this.nodeHash[id];
50997     },
50998
50999     // private
51000     registerNode : function(node){
51001         this.nodeHash[node.id] = node;
51002     },
51003
51004     // private
51005     unregisterNode : function(node){
51006         delete this.nodeHash[node.id];
51007     },
51008
51009     toString : function(){
51010         return "[Tree"+(this.id?" "+this.id:"")+"]";
51011     }
51012 });
51013
51014 /**
51015  * @class Ext.data.Node
51016  * @extends Ext.util.Observable
51017  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
51018  * @cfg {String} id The id for this node. If one is not specified, one is generated.
51019  * @constructor
51020  * @param {Object} attributes The attributes/config for the node
51021  */
51022 Ext.data.Node = function(attributes){
51023     /**
51024      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
51025      * @type {Object}
51026      */
51027     this.attributes = attributes || {};
51028     this.leaf = this.attributes.leaf;
51029     /**
51030      * The node id. @type String
51031      */
51032     this.id = this.attributes.id;
51033     if(!this.id){
51034         this.id = Ext.id(null, "xnode-");
51035         this.attributes.id = this.id;
51036     }
51037     /**
51038      * All child nodes of this node. @type Array
51039      */
51040     this.childNodes = [];
51041     if(!this.childNodes.indexOf){ // indexOf is a must
51042         this.childNodes.indexOf = function(o){
51043             for(var i = 0, len = this.length; i < len; i++){
51044                 if(this[i] == o){
51045                     return i;
51046                 }
51047             }
51048             return -1;
51049         };
51050     }
51051     /**
51052      * The parent node for this node. @type Node
51053      */
51054     this.parentNode = null;
51055     /**
51056      * The first direct child node of this node, or null if this node has no child nodes. @type Node
51057      */
51058     this.firstChild = null;
51059     /**
51060      * The last direct child node of this node, or null if this node has no child nodes. @type Node
51061      */
51062     this.lastChild = null;
51063     /**
51064      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
51065      */
51066     this.previousSibling = null;
51067     /**
51068      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
51069      */
51070     this.nextSibling = null;
51071
51072     this.addEvents({
51073        /**
51074         * @event append
51075         * Fires when a new child node is appended
51076         * @param {Tree} tree The owner tree
51077         * @param {Node} this This node
51078         * @param {Node} node The newly appended node
51079         * @param {Number} index The index of the newly appended node
51080         */
51081        "append" : true,
51082        /**
51083         * @event remove
51084         * Fires when a child node is removed
51085         * @param {Tree} tree The owner tree
51086         * @param {Node} this This node
51087         * @param {Node} node The removed node
51088         */
51089        "remove" : true,
51090        /**
51091         * @event move
51092         * Fires when this node is moved to a new location in the tree
51093         * @param {Tree} tree The owner tree
51094         * @param {Node} this This node
51095         * @param {Node} oldParent The old parent of this node
51096         * @param {Node} newParent The new parent of this node
51097         * @param {Number} index The index it was moved to
51098         */
51099        "move" : true,
51100        /**
51101         * @event insert
51102         * Fires when a new child node is inserted.
51103         * @param {Tree} tree The owner tree
51104         * @param {Node} this This node
51105         * @param {Node} node The child node inserted
51106         * @param {Node} refNode The child node the node was inserted before
51107         */
51108        "insert" : true,
51109        /**
51110         * @event beforeappend
51111         * Fires before a new child is appended, return false to cancel the append.
51112         * @param {Tree} tree The owner tree
51113         * @param {Node} this This node
51114         * @param {Node} node The child node to be appended
51115         */
51116        "beforeappend" : true,
51117        /**
51118         * @event beforeremove
51119         * Fires before a child is removed, return false to cancel the remove.
51120         * @param {Tree} tree The owner tree
51121         * @param {Node} this This node
51122         * @param {Node} node The child node to be removed
51123         */
51124        "beforeremove" : true,
51125        /**
51126         * @event beforemove
51127         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
51128         * @param {Tree} tree The owner tree
51129         * @param {Node} this This node
51130         * @param {Node} oldParent The parent of this node
51131         * @param {Node} newParent The new parent this node is moving to
51132         * @param {Number} index The index it is being moved to
51133         */
51134        "beforemove" : true,
51135        /**
51136         * @event beforeinsert
51137         * Fires before a new child is inserted, return false to cancel the insert.
51138         * @param {Tree} tree The owner tree
51139         * @param {Node} this This node
51140         * @param {Node} node The child node to be inserted
51141         * @param {Node} refNode The child node the node is being inserted before
51142         */
51143        "beforeinsert" : true
51144    });
51145     this.listeners = this.attributes.listeners;
51146     Ext.data.Node.superclass.constructor.call(this);
51147 };
51148
51149 Ext.extend(Ext.data.Node, Ext.util.Observable, {
51150     // private
51151     fireEvent : function(evtName){
51152         // first do standard event for this node
51153         if(Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false){
51154             return false;
51155         }
51156         // then bubble it up to the tree if the event wasn't cancelled
51157         var ot = this.getOwnerTree();
51158         if(ot){
51159             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
51160                 return false;
51161             }
51162         }
51163         return true;
51164     },
51165
51166     /**
51167      * Returns true if this node is a leaf
51168      * @return {Boolean}
51169      */
51170     isLeaf : function(){
51171         return this.leaf === true;
51172     },
51173
51174     // private
51175     setFirstChild : function(node){
51176         this.firstChild = node;
51177     },
51178
51179     //private
51180     setLastChild : function(node){
51181         this.lastChild = node;
51182     },
51183
51184
51185     /**
51186      * Returns true if this node is the last child of its parent
51187      * @return {Boolean}
51188      */
51189     isLast : function(){
51190        return (!this.parentNode ? true : this.parentNode.lastChild == this);
51191     },
51192
51193     /**
51194      * Returns true if this node is the first child of its parent
51195      * @return {Boolean}
51196      */
51197     isFirst : function(){
51198        return (!this.parentNode ? true : this.parentNode.firstChild == this);
51199     },
51200
51201     /**
51202      * Returns true if this node has one or more child nodes, else false.
51203      * @return {Boolean}
51204      */
51205     hasChildNodes : function(){
51206         return !this.isLeaf() && this.childNodes.length > 0;
51207     },
51208
51209     /**
51210      * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
51211      * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
51212      * @return {Boolean}
51213      */
51214     isExpandable : function(){
51215         return this.attributes.expandable || this.hasChildNodes();
51216     },
51217
51218     /**
51219      * Insert node(s) as the last child node of this node.
51220      * @param {Node/Array} node The node or Array of nodes to append
51221      * @return {Node} The appended node if single append, or null if an array was passed
51222      */
51223     appendChild : function(node){
51224         var multi = false;
51225         if(Ext.isArray(node)){
51226             multi = node;
51227         }else if(arguments.length > 1){
51228             multi = arguments;
51229         }
51230         // if passed an array or multiple args do them one by one
51231         if(multi){
51232             for(var i = 0, len = multi.length; i < len; i++) {
51233                 this.appendChild(multi[i]);
51234             }
51235         }else{
51236             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
51237                 return false;
51238             }
51239             var index = this.childNodes.length;
51240             var oldParent = node.parentNode;
51241             // it's a move, make sure we move it cleanly
51242             if(oldParent){
51243                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
51244                     return false;
51245                 }
51246                 oldParent.removeChild(node);
51247             }
51248             index = this.childNodes.length;
51249             if(index === 0){
51250                 this.setFirstChild(node);
51251             }
51252             this.childNodes.push(node);
51253             node.parentNode = this;
51254             var ps = this.childNodes[index-1];
51255             if(ps){
51256                 node.previousSibling = ps;
51257                 ps.nextSibling = node;
51258             }else{
51259                 node.previousSibling = null;
51260             }
51261             node.nextSibling = null;
51262             this.setLastChild(node);
51263             node.setOwnerTree(this.getOwnerTree());
51264             this.fireEvent("append", this.ownerTree, this, node, index);
51265             if(oldParent){
51266                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
51267             }
51268             return node;
51269         }
51270     },
51271
51272     /**
51273      * Removes a child node from this node.
51274      * @param {Node} node The node to remove
51275      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
51276      * @return {Node} The removed node
51277      */
51278     removeChild : function(node, destroy){
51279         var index = this.childNodes.indexOf(node);
51280         if(index == -1){
51281             return false;
51282         }
51283         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
51284             return false;
51285         }
51286
51287         // remove it from childNodes collection
51288         this.childNodes.splice(index, 1);
51289
51290         // update siblings
51291         if(node.previousSibling){
51292             node.previousSibling.nextSibling = node.nextSibling;
51293         }
51294         if(node.nextSibling){
51295             node.nextSibling.previousSibling = node.previousSibling;
51296         }
51297
51298         // update child refs
51299         if(this.firstChild == node){
51300             this.setFirstChild(node.nextSibling);
51301         }
51302         if(this.lastChild == node){
51303             this.setLastChild(node.previousSibling);
51304         }
51305
51306         this.fireEvent("remove", this.ownerTree, this, node);
51307         if(destroy){
51308             node.destroy(true);
51309         }else{
51310             node.clear();
51311         }
51312         return node;
51313     },
51314
51315     // private
51316     clear : function(destroy){
51317         // clear any references from the node
51318         this.setOwnerTree(null, destroy);
51319         this.parentNode = this.previousSibling = this.nextSibling = null;
51320         if(destroy){
51321             this.firstChild = this.lastChild = null;
51322         }
51323     },
51324
51325     /**
51326      * Destroys the node.
51327      */
51328     destroy : function(/* private */ silent){
51329         /*
51330          * Silent is to be used in a number of cases
51331          * 1) When setRootNode is called.
51332          * 2) When destroy on the tree is called
51333          * 3) For destroying child nodes on a node
51334          */
51335         if(silent === true){
51336             this.purgeListeners();
51337             this.clear(true);
51338             Ext.each(this.childNodes, function(n){
51339                 n.destroy(true);
51340             });
51341             this.childNodes = null;
51342         }else{
51343             this.remove(true);
51344         }
51345     },
51346
51347     /**
51348      * Inserts the first node before the second node in this nodes childNodes collection.
51349      * @param {Node} node The node to insert
51350      * @param {Node} refNode The node to insert before (if null the node is appended)
51351      * @return {Node} The inserted node
51352      */
51353     insertBefore : function(node, refNode){
51354         if(!refNode){ // like standard Dom, refNode can be null for append
51355             return this.appendChild(node);
51356         }
51357         // nothing to do
51358         if(node == refNode){
51359             return false;
51360         }
51361
51362         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
51363             return false;
51364         }
51365         var index = this.childNodes.indexOf(refNode);
51366         var oldParent = node.parentNode;
51367         var refIndex = index;
51368
51369         // when moving internally, indexes will change after remove
51370         if(oldParent == this && this.childNodes.indexOf(node) < index){
51371             refIndex--;
51372         }
51373
51374         // it's a move, make sure we move it cleanly
51375         if(oldParent){
51376             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
51377                 return false;
51378             }
51379             oldParent.removeChild(node);
51380         }
51381         if(refIndex === 0){
51382             this.setFirstChild(node);
51383         }
51384         this.childNodes.splice(refIndex, 0, node);
51385         node.parentNode = this;
51386         var ps = this.childNodes[refIndex-1];
51387         if(ps){
51388             node.previousSibling = ps;
51389             ps.nextSibling = node;
51390         }else{
51391             node.previousSibling = null;
51392         }
51393         node.nextSibling = refNode;
51394         refNode.previousSibling = node;
51395         node.setOwnerTree(this.getOwnerTree());
51396         this.fireEvent("insert", this.ownerTree, this, node, refNode);
51397         if(oldParent){
51398             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
51399         }
51400         return node;
51401     },
51402
51403     /**
51404      * Removes this node from its parent
51405      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
51406      * @return {Node} this
51407      */
51408     remove : function(destroy){
51409         if (this.parentNode) {
51410             this.parentNode.removeChild(this, destroy);
51411         }
51412         return this;
51413     },
51414
51415     /**
51416      * Removes all child nodes from this node.
51417      * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
51418      * @return {Node} this
51419      */
51420     removeAll : function(destroy){
51421         var cn = this.childNodes,
51422             n;
51423         while((n = cn[0])){
51424             this.removeChild(n, destroy);
51425         }
51426         return this;
51427     },
51428
51429     /**
51430      * Returns the child node at the specified index.
51431      * @param {Number} index
51432      * @return {Node}
51433      */
51434     item : function(index){
51435         return this.childNodes[index];
51436     },
51437
51438     /**
51439      * Replaces one child node in this node with another.
51440      * @param {Node} newChild The replacement node
51441      * @param {Node} oldChild The node to replace
51442      * @return {Node} The replaced node
51443      */
51444     replaceChild : function(newChild, oldChild){
51445         var s = oldChild ? oldChild.nextSibling : null;
51446         this.removeChild(oldChild);
51447         this.insertBefore(newChild, s);
51448         return oldChild;
51449     },
51450
51451     /**
51452      * Returns the index of a child node
51453      * @param {Node} node
51454      * @return {Number} The index of the node or -1 if it was not found
51455      */
51456     indexOf : function(child){
51457         return this.childNodes.indexOf(child);
51458     },
51459
51460     /**
51461      * Returns the tree this node is in.
51462      * @return {Tree}
51463      */
51464     getOwnerTree : function(){
51465         // if it doesn't have one, look for one
51466         if(!this.ownerTree){
51467             var p = this;
51468             while(p){
51469                 if(p.ownerTree){
51470                     this.ownerTree = p.ownerTree;
51471                     break;
51472                 }
51473                 p = p.parentNode;
51474             }
51475         }
51476         return this.ownerTree;
51477     },
51478
51479     /**
51480      * Returns depth of this node (the root node has a depth of 0)
51481      * @return {Number}
51482      */
51483     getDepth : function(){
51484         var depth = 0;
51485         var p = this;
51486         while(p.parentNode){
51487             ++depth;
51488             p = p.parentNode;
51489         }
51490         return depth;
51491     },
51492
51493     // private
51494     setOwnerTree : function(tree, destroy){
51495         // if it is a move, we need to update everyone
51496         if(tree != this.ownerTree){
51497             if(this.ownerTree){
51498                 this.ownerTree.unregisterNode(this);
51499             }
51500             this.ownerTree = tree;
51501             // If we're destroying, we don't need to recurse since it will be called on each child node
51502             if(destroy !== true){
51503                 Ext.each(this.childNodes, function(n){
51504                     n.setOwnerTree(tree);
51505                 });
51506             }
51507             if(tree){
51508                 tree.registerNode(this);
51509             }
51510         }
51511     },
51512
51513     /**
51514      * Changes the id of this node.
51515      * @param {String} id The new id for the node.
51516      */
51517     setId: function(id){
51518         if(id !== this.id){
51519             var t = this.ownerTree;
51520             if(t){
51521                 t.unregisterNode(this);
51522             }
51523             this.id = this.attributes.id = id;
51524             if(t){
51525                 t.registerNode(this);
51526             }
51527             this.onIdChange(id);
51528         }
51529     },
51530
51531     // private
51532     onIdChange: Ext.emptyFn,
51533
51534     /**
51535      * Returns the path for this node. The path can be used to expand or select this node programmatically.
51536      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
51537      * @return {String} The path
51538      */
51539     getPath : function(attr){
51540         attr = attr || "id";
51541         var p = this.parentNode;
51542         var b = [this.attributes[attr]];
51543         while(p){
51544             b.unshift(p.attributes[attr]);
51545             p = p.parentNode;
51546         }
51547         var sep = this.getOwnerTree().pathSeparator;
51548         return sep + b.join(sep);
51549     },
51550
51551     /**
51552      * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
51553      * will be the args provided or the current node. If the function returns false at any point,
51554      * the bubble is stopped.
51555      * @param {Function} fn The function to call
51556      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
51557      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
51558      */
51559     bubble : function(fn, scope, args){
51560         var p = this;
51561         while(p){
51562             if(fn.apply(scope || p, args || [p]) === false){
51563                 break;
51564             }
51565             p = p.parentNode;
51566         }
51567     },
51568
51569     /**
51570      * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
51571      * will be the args provided or the current node. If the function returns false at any point,
51572      * the cascade is stopped on that branch.
51573      * @param {Function} fn The function to call
51574      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
51575      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
51576      */
51577     cascade : function(fn, scope, args){
51578         if(fn.apply(scope || this, args || [this]) !== false){
51579             var cs = this.childNodes;
51580             for(var i = 0, len = cs.length; i < len; i++) {
51581                 cs[i].cascade(fn, scope, args);
51582             }
51583         }
51584     },
51585
51586     /**
51587      * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
51588      * will be the args provided or the current node. If the function returns false at any point,
51589      * the iteration stops.
51590      * @param {Function} fn The function to call
51591      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
51592      * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
51593      */
51594     eachChild : function(fn, scope, args){
51595         var cs = this.childNodes;
51596         for(var i = 0, len = cs.length; i < len; i++) {
51597             if(fn.apply(scope || this, args || [cs[i]]) === false){
51598                 break;
51599             }
51600         }
51601     },
51602
51603     /**
51604      * Finds the first child that has the attribute with the specified value.
51605      * @param {String} attribute The attribute name
51606      * @param {Mixed} value The value to search for
51607      * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
51608      * @return {Node} The found child or null if none was found
51609      */
51610     findChild : function(attribute, value, deep){
51611         return this.findChildBy(function(){
51612             return this.attributes[attribute] == value;
51613         }, null, deep);
51614     },
51615
51616     /**
51617      * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
51618      * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
51619      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
51620      * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
51621      * @return {Node} The found child or null if none was found
51622      */
51623     findChildBy : function(fn, scope, deep){
51624         var cs = this.childNodes,
51625             len = cs.length,
51626             i = 0,
51627             n,
51628             res;
51629         for(; i < len; i++){
51630             n = cs[i];
51631             if(fn.call(scope || n, n) === true){
51632                 return n;
51633             }else if (deep){
51634                 res = n.findChildBy(fn, scope, deep);
51635                 if(res != null){
51636                     return res;
51637                 }
51638             }
51639             
51640         }
51641         return null;
51642     },
51643
51644     /**
51645      * Sorts this nodes children using the supplied sort function.
51646      * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
51647      * @param {Object} scope (optional)The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
51648      */
51649     sort : function(fn, scope){
51650         var cs = this.childNodes;
51651         var len = cs.length;
51652         if(len > 0){
51653             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
51654             cs.sort(sortFn);
51655             for(var i = 0; i < len; i++){
51656                 var n = cs[i];
51657                 n.previousSibling = cs[i-1];
51658                 n.nextSibling = cs[i+1];
51659                 if(i === 0){
51660                     this.setFirstChild(n);
51661                 }
51662                 if(i == len-1){
51663                     this.setLastChild(n);
51664                 }
51665             }
51666         }
51667     },
51668
51669     /**
51670      * Returns true if this node is an ancestor (at any point) of the passed node.
51671      * @param {Node} node
51672      * @return {Boolean}
51673      */
51674     contains : function(node){
51675         return node.isAncestor(this);
51676     },
51677
51678     /**
51679      * Returns true if the passed node is an ancestor (at any point) of this node.
51680      * @param {Node} node
51681      * @return {Boolean}
51682      */
51683     isAncestor : function(node){
51684         var p = this.parentNode;
51685         while(p){
51686             if(p == node){
51687                 return true;
51688             }
51689             p = p.parentNode;
51690         }
51691         return false;
51692     },
51693
51694     toString : function(){
51695         return "[Node"+(this.id?" "+this.id:"")+"]";
51696     }
51697 });/**
51698  * @class Ext.tree.TreeNode
51699  * @extends Ext.data.Node
51700  * @cfg {String} text The text for this node
51701  * @cfg {Boolean} expanded true to start the node expanded
51702  * @cfg {Boolean} allowDrag False to make this node undraggable if {@link #draggable} = true (defaults to true)
51703  * @cfg {Boolean} allowDrop False if this node cannot have child nodes dropped on it (defaults to true)
51704  * @cfg {Boolean} disabled true to start the node disabled
51705  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
51706  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
51707  * @cfg {String} cls A css class to be added to the node
51708  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
51709  * @cfg {String} href URL of the link used for the node (defaults to #)
51710  * @cfg {String} hrefTarget target frame for the link
51711  * @cfg {Boolean} hidden True to render hidden. (Defaults to false).
51712  * @cfg {String} qtip An Ext QuickTip for the node
51713  * @cfg {Boolean} expandable If set to true, the node will always show a plus/minus icon, even when empty
51714  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
51715  * @cfg {Boolean} singleClickExpand True for single click expand on this node
51716  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Ext.tree.TreeNodeUI)
51717  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
51718  * (defaults to undefined with no checkbox rendered)
51719  * @cfg {Boolean} draggable True to make this node draggable (defaults to false)
51720  * @cfg {Boolean} isTarget False to not allow this node to act as a drop target (defaults to true)
51721  * @cfg {Boolean} allowChildren False to not allow this node to have child nodes (defaults to true)
51722  * @cfg {Boolean} editable False to not allow this node to be edited by an {@link Ext.tree.TreeEditor} (defaults to true)
51723  * @constructor
51724  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
51725  */
51726 Ext.tree.TreeNode = function(attributes){
51727     attributes = attributes || {};
51728     if(Ext.isString(attributes)){
51729         attributes = {text: attributes};
51730     }
51731     this.childrenRendered = false;
51732     this.rendered = false;
51733     Ext.tree.TreeNode.superclass.constructor.call(this, attributes);
51734     this.expanded = attributes.expanded === true;
51735     this.isTarget = attributes.isTarget !== false;
51736     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
51737     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
51738
51739     /**
51740      * Read-only. The text for this node. To change it use <code>{@link #setText}</code>.
51741      * @type String
51742      */
51743     this.text = attributes.text;
51744     /**
51745      * True if this node is disabled.
51746      * @type Boolean
51747      */
51748     this.disabled = attributes.disabled === true;
51749     /**
51750      * True if this node is hidden.
51751      * @type Boolean
51752      */
51753     this.hidden = attributes.hidden === true;
51754
51755     this.addEvents(
51756         /**
51757         * @event textchange
51758         * Fires when the text for this node is changed
51759         * @param {Node} this This node
51760         * @param {String} text The new text
51761         * @param {String} oldText The old text
51762         */
51763         'textchange',
51764         /**
51765         * @event beforeexpand
51766         * Fires before this node is expanded, return false to cancel.
51767         * @param {Node} this This node
51768         * @param {Boolean} deep
51769         * @param {Boolean} anim
51770         */
51771         'beforeexpand',
51772         /**
51773         * @event beforecollapse
51774         * Fires before this node is collapsed, return false to cancel.
51775         * @param {Node} this This node
51776         * @param {Boolean} deep
51777         * @param {Boolean} anim
51778         */
51779         'beforecollapse',
51780         /**
51781         * @event expand
51782         * Fires when this node is expanded
51783         * @param {Node} this This node
51784         */
51785         'expand',
51786         /**
51787         * @event disabledchange
51788         * Fires when the disabled status of this node changes
51789         * @param {Node} this This node
51790         * @param {Boolean} disabled
51791         */
51792         'disabledchange',
51793         /**
51794         * @event collapse
51795         * Fires when this node is collapsed
51796         * @param {Node} this This node
51797         */
51798         'collapse',
51799         /**
51800         * @event beforeclick
51801         * Fires before click processing. Return false to cancel the default action.
51802         * @param {Node} this This node
51803         * @param {Ext.EventObject} e The event object
51804         */
51805         'beforeclick',
51806         /**
51807         * @event click
51808         * Fires when this node is clicked
51809         * @param {Node} this This node
51810         * @param {Ext.EventObject} e The event object
51811         */
51812         'click',
51813         /**
51814         * @event checkchange
51815         * Fires when a node with a checkbox's checked property changes
51816         * @param {Node} this This node
51817         * @param {Boolean} checked
51818         */
51819         'checkchange',
51820         /**
51821         * @event beforedblclick
51822         * Fires before double click processing. Return false to cancel the default action.
51823         * @param {Node} this This node
51824         * @param {Ext.EventObject} e The event object
51825         */
51826         'beforedblclick',
51827         /**
51828         * @event dblclick
51829         * Fires when this node is double clicked
51830         * @param {Node} this This node
51831         * @param {Ext.EventObject} e The event object
51832         */
51833         'dblclick',
51834         /**
51835         * @event contextmenu
51836         * Fires when this node is right clicked
51837         * @param {Node} this This node
51838         * @param {Ext.EventObject} e The event object
51839         */
51840         'contextmenu',
51841         /**
51842         * @event beforechildrenrendered
51843         * Fires right before the child nodes for this node are rendered
51844         * @param {Node} this This node
51845         */
51846         'beforechildrenrendered'
51847     );
51848
51849     var uiClass = this.attributes.uiProvider || this.defaultUI || Ext.tree.TreeNodeUI;
51850
51851     /**
51852      * Read-only. The UI for this node
51853      * @type TreeNodeUI
51854      */
51855     this.ui = new uiClass(this);
51856 };
51857 Ext.extend(Ext.tree.TreeNode, Ext.data.Node, {
51858     preventHScroll : true,
51859     /**
51860      * Returns true if this node is expanded
51861      * @return {Boolean}
51862      */
51863     isExpanded : function(){
51864         return this.expanded;
51865     },
51866
51867 /**
51868  * Returns the UI object for this node.
51869  * @return {TreeNodeUI} The object which is providing the user interface for this tree
51870  * node. Unless otherwise specified in the {@link #uiProvider}, this will be an instance
51871  * of {@link Ext.tree.TreeNodeUI}
51872  */
51873     getUI : function(){
51874         return this.ui;
51875     },
51876
51877     getLoader : function(){
51878         var owner;
51879         return this.loader || ((owner = this.getOwnerTree()) && owner.loader ? owner.loader : (this.loader = new Ext.tree.TreeLoader()));
51880     },
51881
51882     // private override
51883     setFirstChild : function(node){
51884         var of = this.firstChild;
51885         Ext.tree.TreeNode.superclass.setFirstChild.call(this, node);
51886         if(this.childrenRendered && of && node != of){
51887             of.renderIndent(true, true);
51888         }
51889         if(this.rendered){
51890             this.renderIndent(true, true);
51891         }
51892     },
51893
51894     // private override
51895     setLastChild : function(node){
51896         var ol = this.lastChild;
51897         Ext.tree.TreeNode.superclass.setLastChild.call(this, node);
51898         if(this.childrenRendered && ol && node != ol){
51899             ol.renderIndent(true, true);
51900         }
51901         if(this.rendered){
51902             this.renderIndent(true, true);
51903         }
51904     },
51905
51906     // these methods are overridden to provide lazy rendering support
51907     // private override
51908     appendChild : function(n){
51909         if(!n.render && !Ext.isArray(n)){
51910             n = this.getLoader().createNode(n);
51911         }
51912         var node = Ext.tree.TreeNode.superclass.appendChild.call(this, n);
51913         if(node && this.childrenRendered){
51914             node.render();
51915         }
51916         this.ui.updateExpandIcon();
51917         return node;
51918     },
51919
51920     // private override
51921     removeChild : function(node, destroy){
51922         this.ownerTree.getSelectionModel().unselect(node);
51923         Ext.tree.TreeNode.superclass.removeChild.apply(this, arguments);
51924         // only update the ui if we're not destroying
51925         if(!destroy){
51926             // if it's been rendered remove dom node
51927             if(node.ui.rendered){
51928                 node.ui.remove();
51929             }
51930             if(this.childNodes.length < 1){
51931                 this.collapse(false, false);
51932             }else{
51933                 this.ui.updateExpandIcon();
51934             }
51935             if(!this.firstChild && !this.isHiddenRoot()){
51936                 this.childrenRendered = false;
51937             }
51938         }
51939         return node;
51940     },
51941
51942     // private override
51943     insertBefore : function(node, refNode){
51944         if(!node.render){
51945             node = this.getLoader().createNode(node);
51946         }
51947         var newNode = Ext.tree.TreeNode.superclass.insertBefore.call(this, node, refNode);
51948         if(newNode && refNode && this.childrenRendered){
51949             node.render();
51950         }
51951         this.ui.updateExpandIcon();
51952         return newNode;
51953     },
51954
51955     /**
51956      * Sets the text for this node
51957      * @param {String} text
51958      */
51959     setText : function(text){
51960         var oldText = this.text;
51961         this.text = this.attributes.text = text;
51962         if(this.rendered){ // event without subscribing
51963             this.ui.onTextChange(this, text, oldText);
51964         }
51965         this.fireEvent('textchange', this, text, oldText);
51966     },
51967
51968     /**
51969      * Triggers selection of this node
51970      */
51971     select : function(){
51972         var t = this.getOwnerTree();
51973         if(t){
51974             t.getSelectionModel().select(this);
51975         }
51976     },
51977
51978     /**
51979      * Triggers deselection of this node
51980      * @param {Boolean} silent (optional) True to stop selection change events from firing.
51981      */
51982     unselect : function(silent){
51983         var t = this.getOwnerTree();
51984         if(t){
51985             t.getSelectionModel().unselect(this, silent);
51986         }
51987     },
51988
51989     /**
51990      * Returns true if this node is selected
51991      * @return {Boolean}
51992      */
51993     isSelected : function(){
51994         var t = this.getOwnerTree();
51995         return t ? t.getSelectionModel().isSelected(this) : false;
51996     },
51997
51998     /**
51999      * Expand this node.
52000      * @param {Boolean} deep (optional) True to expand all children as well
52001      * @param {Boolean} anim (optional) false to cancel the default animation
52002      * @param {Function} callback (optional) A callback to be called when
52003      * expanding this node completes (does not wait for deep expand to complete).
52004      * Called with 1 parameter, this node.
52005      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
52006      */
52007     expand : function(deep, anim, callback, scope){
52008         if(!this.expanded){
52009             if(this.fireEvent('beforeexpand', this, deep, anim) === false){
52010                 return;
52011             }
52012             if(!this.childrenRendered){
52013                 this.renderChildren();
52014             }
52015             this.expanded = true;
52016             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
52017                 this.ui.animExpand(function(){
52018                     this.fireEvent('expand', this);
52019                     this.runCallback(callback, scope || this, [this]);
52020                     if(deep === true){
52021                         this.expandChildNodes(true);
52022                     }
52023                 }.createDelegate(this));
52024                 return;
52025             }else{
52026                 this.ui.expand();
52027                 this.fireEvent('expand', this);
52028                 this.runCallback(callback, scope || this, [this]);
52029             }
52030         }else{
52031            this.runCallback(callback, scope || this, [this]);
52032         }
52033         if(deep === true){
52034             this.expandChildNodes(true);
52035         }
52036     },
52037
52038     runCallback : function(cb, scope, args){
52039         if(Ext.isFunction(cb)){
52040             cb.apply(scope, args);
52041         }
52042     },
52043
52044     isHiddenRoot : function(){
52045         return this.isRoot && !this.getOwnerTree().rootVisible;
52046     },
52047
52048     /**
52049      * Collapse this node.
52050      * @param {Boolean} deep (optional) True to collapse all children as well
52051      * @param {Boolean} anim (optional) false to cancel the default animation
52052      * @param {Function} callback (optional) A callback to be called when
52053      * expanding this node completes (does not wait for deep expand to complete).
52054      * Called with 1 parameter, this node.
52055      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
52056      */
52057     collapse : function(deep, anim, callback, scope){
52058         if(this.expanded && !this.isHiddenRoot()){
52059             if(this.fireEvent('beforecollapse', this, deep, anim) === false){
52060                 return;
52061             }
52062             this.expanded = false;
52063             if((this.getOwnerTree().animate && anim !== false) || anim){
52064                 this.ui.animCollapse(function(){
52065                     this.fireEvent('collapse', this);
52066                     this.runCallback(callback, scope || this, [this]);
52067                     if(deep === true){
52068                         this.collapseChildNodes(true);
52069                     }
52070                 }.createDelegate(this));
52071                 return;
52072             }else{
52073                 this.ui.collapse();
52074                 this.fireEvent('collapse', this);
52075                 this.runCallback(callback, scope || this, [this]);
52076             }
52077         }else if(!this.expanded){
52078             this.runCallback(callback, scope || this, [this]);
52079         }
52080         if(deep === true){
52081             var cs = this.childNodes;
52082             for(var i = 0, len = cs.length; i < len; i++) {
52083                 cs[i].collapse(true, false);
52084             }
52085         }
52086     },
52087
52088     // private
52089     delayedExpand : function(delay){
52090         if(!this.expandProcId){
52091             this.expandProcId = this.expand.defer(delay, this);
52092         }
52093     },
52094
52095     // private
52096     cancelExpand : function(){
52097         if(this.expandProcId){
52098             clearTimeout(this.expandProcId);
52099         }
52100         this.expandProcId = false;
52101     },
52102
52103     /**
52104      * Toggles expanded/collapsed state of the node
52105      */
52106     toggle : function(){
52107         if(this.expanded){
52108             this.collapse();
52109         }else{
52110             this.expand();
52111         }
52112     },
52113
52114     /**
52115      * Ensures all parent nodes are expanded, and if necessary, scrolls
52116      * the node into view.
52117      * @param {Function} callback (optional) A function to call when the node has been made visible.
52118      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this TreeNode.
52119      */
52120     ensureVisible : function(callback, scope){
52121         var tree = this.getOwnerTree();
52122         tree.expandPath(this.parentNode ? this.parentNode.getPath() : this.getPath(), false, function(){
52123             var node = tree.getNodeById(this.id);  // Somehow if we don't do this, we lose changes that happened to node in the meantime
52124             tree.getTreeEl().scrollChildIntoView(node.ui.anchor);
52125             this.runCallback(callback, scope || this, [this]);
52126         }.createDelegate(this));
52127     },
52128
52129     /**
52130      * Expand all child nodes
52131      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
52132      */
52133     expandChildNodes : function(deep){
52134         var cs = this.childNodes;
52135         for(var i = 0, len = cs.length; i < len; i++) {
52136                 cs[i].expand(deep);
52137         }
52138     },
52139
52140     /**
52141      * Collapse all child nodes
52142      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
52143      */
52144     collapseChildNodes : function(deep){
52145         var cs = this.childNodes;
52146         for(var i = 0, len = cs.length; i < len; i++) {
52147                 cs[i].collapse(deep);
52148         }
52149     },
52150
52151     /**
52152      * Disables this node
52153      */
52154     disable : function(){
52155         this.disabled = true;
52156         this.unselect();
52157         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
52158             this.ui.onDisableChange(this, true);
52159         }
52160         this.fireEvent('disabledchange', this, true);
52161     },
52162
52163     /**
52164      * Enables this node
52165      */
52166     enable : function(){
52167         this.disabled = false;
52168         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
52169             this.ui.onDisableChange(this, false);
52170         }
52171         this.fireEvent('disabledchange', this, false);
52172     },
52173
52174     // private
52175     renderChildren : function(suppressEvent){
52176         if(suppressEvent !== false){
52177             this.fireEvent('beforechildrenrendered', this);
52178         }
52179         var cs = this.childNodes;
52180         for(var i = 0, len = cs.length; i < len; i++){
52181             cs[i].render(true);
52182         }
52183         this.childrenRendered = true;
52184     },
52185
52186     // private
52187     sort : function(fn, scope){
52188         Ext.tree.TreeNode.superclass.sort.apply(this, arguments);
52189         if(this.childrenRendered){
52190             var cs = this.childNodes;
52191             for(var i = 0, len = cs.length; i < len; i++){
52192                 cs[i].render(true);
52193             }
52194         }
52195     },
52196
52197     // private
52198     render : function(bulkRender){
52199         this.ui.render(bulkRender);
52200         if(!this.rendered){
52201             // make sure it is registered
52202             this.getOwnerTree().registerNode(this);
52203             this.rendered = true;
52204             if(this.expanded){
52205                 this.expanded = false;
52206                 this.expand(false, false);
52207             }
52208         }
52209     },
52210
52211     // private
52212     renderIndent : function(deep, refresh){
52213         if(refresh){
52214             this.ui.childIndent = null;
52215         }
52216         this.ui.renderIndent();
52217         if(deep === true && this.childrenRendered){
52218             var cs = this.childNodes;
52219             for(var i = 0, len = cs.length; i < len; i++){
52220                 cs[i].renderIndent(true, refresh);
52221             }
52222         }
52223     },
52224
52225     beginUpdate : function(){
52226         this.childrenRendered = false;
52227     },
52228
52229     endUpdate : function(){
52230         if(this.expanded && this.rendered){
52231             this.renderChildren();
52232         }
52233     },
52234
52235     //inherit docs
52236     destroy : function(silent){
52237         if(silent === true){
52238             this.unselect(true);
52239         }
52240         Ext.tree.TreeNode.superclass.destroy.call(this, silent);
52241         Ext.destroy(this.ui, this.loader);
52242         this.ui = this.loader = null;
52243     },
52244
52245     // private
52246     onIdChange : function(id){
52247         this.ui.onIdChange(id);
52248     }
52249 });
52250
52251 Ext.tree.TreePanel.nodeTypes.node = Ext.tree.TreeNode;/**
52252  * @class Ext.tree.AsyncTreeNode
52253  * @extends Ext.tree.TreeNode
52254  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
52255  * @constructor
52256  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
52257  */
52258  Ext.tree.AsyncTreeNode = function(config){
52259     this.loaded = config && config.loaded === true;
52260     this.loading = false;
52261     Ext.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
52262     /**
52263     * @event beforeload
52264     * Fires before this node is loaded, return false to cancel
52265     * @param {Node} this This node
52266     */
52267     this.addEvents('beforeload', 'load');
52268     /**
52269     * @event load
52270     * Fires when this node is loaded
52271     * @param {Node} this This node
52272     */
52273     /**
52274      * The loader used by this node (defaults to using the tree's defined loader)
52275      * @type TreeLoader
52276      * @property loader
52277      */
52278 };
52279 Ext.extend(Ext.tree.AsyncTreeNode, Ext.tree.TreeNode, {
52280     expand : function(deep, anim, callback, scope){
52281         if(this.loading){ // if an async load is already running, waiting til it's done
52282             var timer;
52283             var f = function(){
52284                 if(!this.loading){ // done loading
52285                     clearInterval(timer);
52286                     this.expand(deep, anim, callback, scope);
52287                 }
52288             }.createDelegate(this);
52289             timer = setInterval(f, 200);
52290             return;
52291         }
52292         if(!this.loaded){
52293             if(this.fireEvent("beforeload", this) === false){
52294                 return;
52295             }
52296             this.loading = true;
52297             this.ui.beforeLoad(this);
52298             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
52299             if(loader){
52300                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback, scope]), this);
52301                 return;
52302             }
52303         }
52304         Ext.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback, scope);
52305     },
52306     
52307     /**
52308      * Returns true if this node is currently loading
52309      * @return {Boolean}
52310      */
52311     isLoading : function(){
52312         return this.loading;  
52313     },
52314     
52315     loadComplete : function(deep, anim, callback, scope){
52316         this.loading = false;
52317         this.loaded = true;
52318         this.ui.afterLoad(this);
52319         this.fireEvent("load", this);
52320         this.expand(deep, anim, callback, scope);
52321     },
52322     
52323     /**
52324      * Returns true if this node has been loaded
52325      * @return {Boolean}
52326      */
52327     isLoaded : function(){
52328         return this.loaded;
52329     },
52330     
52331     hasChildNodes : function(){
52332         if(!this.isLeaf() && !this.loaded){
52333             return true;
52334         }else{
52335             return Ext.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
52336         }
52337     },
52338
52339     /**
52340      * Trigger a reload for this node
52341      * @param {Function} callback
52342      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Node.
52343      */
52344     reload : function(callback, scope){
52345         this.collapse(false, false);
52346         while(this.firstChild){
52347             this.removeChild(this.firstChild).destroy();
52348         }
52349         this.childrenRendered = false;
52350         this.loaded = false;
52351         if(this.isHiddenRoot()){
52352             this.expanded = false;
52353         }
52354         this.expand(false, false, callback, scope);
52355     }
52356 });
52357
52358 Ext.tree.TreePanel.nodeTypes.async = Ext.tree.AsyncTreeNode;/**
52359  * @class Ext.tree.TreeNodeUI
52360  * This class provides the default UI implementation for Ext TreeNodes.
52361  * The TreeNode UI implementation is separate from the
52362  * tree implementation, and allows customizing of the appearance of
52363  * tree nodes.<br>
52364  * <p>
52365  * If you are customizing the Tree's user interface, you
52366  * may need to extend this class, but you should never need to instantiate this class.<br>
52367  * <p>
52368  * This class provides access to the user interface components of an Ext TreeNode, through
52369  * {@link Ext.tree.TreeNode#getUI}
52370  */
52371 Ext.tree.TreeNodeUI = function(node){
52372     this.node = node;
52373     this.rendered = false;
52374     this.animating = false;
52375     this.wasLeaf = true;
52376     this.ecc = 'x-tree-ec-icon x-tree-elbow';
52377     this.emptyIcon = Ext.BLANK_IMAGE_URL;
52378 };
52379
52380 Ext.tree.TreeNodeUI.prototype = {
52381     // private
52382     removeChild : function(node){
52383         if(this.rendered){
52384             this.ctNode.removeChild(node.ui.getEl());
52385         }
52386     },
52387
52388     // private
52389     beforeLoad : function(){
52390          this.addClass("x-tree-node-loading");
52391     },
52392
52393     // private
52394     afterLoad : function(){
52395          this.removeClass("x-tree-node-loading");
52396     },
52397
52398     // private
52399     onTextChange : function(node, text, oldText){
52400         if(this.rendered){
52401             this.textNode.innerHTML = text;
52402         }
52403     },
52404
52405     // private
52406     onDisableChange : function(node, state){
52407         this.disabled = state;
52408         if (this.checkbox) {
52409             this.checkbox.disabled = state;
52410         }
52411         if(state){
52412             this.addClass("x-tree-node-disabled");
52413         }else{
52414             this.removeClass("x-tree-node-disabled");
52415         }
52416     },
52417
52418     // private
52419     onSelectedChange : function(state){
52420         if(state){
52421             this.focus();
52422             this.addClass("x-tree-selected");
52423         }else{
52424             //this.blur();
52425             this.removeClass("x-tree-selected");
52426         }
52427     },
52428
52429     // private
52430     onMove : function(tree, node, oldParent, newParent, index, refNode){
52431         this.childIndent = null;
52432         if(this.rendered){
52433             var targetNode = newParent.ui.getContainer();
52434             if(!targetNode){//target not rendered
52435                 this.holder = document.createElement("div");
52436                 this.holder.appendChild(this.wrap);
52437                 return;
52438             }
52439             var insertBefore = refNode ? refNode.ui.getEl() : null;
52440             if(insertBefore){
52441                 targetNode.insertBefore(this.wrap, insertBefore);
52442             }else{
52443                 targetNode.appendChild(this.wrap);
52444             }
52445             this.node.renderIndent(true, oldParent != newParent);
52446         }
52447     },
52448
52449 /**
52450  * Adds one or more CSS classes to the node's UI element.
52451  * Duplicate classes are automatically filtered out.
52452  * @param {String/Array} className The CSS class to add, or an array of classes
52453  */
52454     addClass : function(cls){
52455         if(this.elNode){
52456             Ext.fly(this.elNode).addClass(cls);
52457         }
52458     },
52459
52460 /**
52461  * Removes one or more CSS classes from the node's UI element.
52462  * @param {String/Array} className The CSS class to remove, or an array of classes
52463  */
52464     removeClass : function(cls){
52465         if(this.elNode){
52466             Ext.fly(this.elNode).removeClass(cls);
52467         }
52468     },
52469
52470     // private
52471     remove : function(){
52472         if(this.rendered){
52473             this.holder = document.createElement("div");
52474             this.holder.appendChild(this.wrap);
52475         }
52476     },
52477
52478     // private
52479     fireEvent : function(){
52480         return this.node.fireEvent.apply(this.node, arguments);
52481     },
52482
52483     // private
52484     initEvents : function(){
52485         this.node.on("move", this.onMove, this);
52486
52487         if(this.node.disabled){
52488             this.onDisableChange(this.node, true);
52489         }
52490         if(this.node.hidden){
52491             this.hide();
52492         }
52493         var ot = this.node.getOwnerTree();
52494         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
52495         if(dd && (!this.node.isRoot || ot.rootVisible)){
52496             Ext.dd.Registry.register(this.elNode, {
52497                 node: this.node,
52498                 handles: this.getDDHandles(),
52499                 isHandle: false
52500             });
52501         }
52502     },
52503
52504     // private
52505     getDDHandles : function(){
52506         return [this.iconNode, this.textNode, this.elNode];
52507     },
52508
52509 /**
52510  * Hides this node.
52511  */
52512     hide : function(){
52513         this.node.hidden = true;
52514         if(this.wrap){
52515             this.wrap.style.display = "none";
52516         }
52517     },
52518
52519 /**
52520  * Shows this node.
52521  */
52522     show : function(){
52523         this.node.hidden = false;
52524         if(this.wrap){
52525             this.wrap.style.display = "";
52526         }
52527     },
52528
52529     // private
52530     onContextMenu : function(e){
52531         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
52532             e.preventDefault();
52533             this.focus();
52534             this.fireEvent("contextmenu", this.node, e);
52535         }
52536     },
52537
52538     // private
52539     onClick : function(e){
52540         if(this.dropping){
52541             e.stopEvent();
52542             return;
52543         }
52544         if(this.fireEvent("beforeclick", this.node, e) !== false){
52545             var a = e.getTarget('a');
52546             if(!this.disabled && this.node.attributes.href && a){
52547                 this.fireEvent("click", this.node, e);
52548                 return;
52549             }else if(a && e.ctrlKey){
52550                 e.stopEvent();
52551             }
52552             e.preventDefault();
52553             if(this.disabled){
52554                 return;
52555             }
52556
52557             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
52558                 this.node.toggle();
52559             }
52560
52561             this.fireEvent("click", this.node, e);
52562         }else{
52563             e.stopEvent();
52564         }
52565     },
52566
52567     // private
52568     onDblClick : function(e){
52569         e.preventDefault();
52570         if(this.disabled){
52571             return;
52572         }
52573         if(this.fireEvent("beforedblclick", this.node, e) !== false){
52574             if(this.checkbox){
52575                 this.toggleCheck();
52576             }
52577             if(!this.animating && this.node.isExpandable()){
52578                 this.node.toggle();
52579             }
52580             this.fireEvent("dblclick", this.node, e);
52581         }
52582     },
52583
52584     onOver : function(e){
52585         this.addClass('x-tree-node-over');
52586     },
52587
52588     onOut : function(e){
52589         this.removeClass('x-tree-node-over');
52590     },
52591
52592     // private
52593     onCheckChange : function(){
52594         var checked = this.checkbox.checked;
52595         // fix for IE6
52596         this.checkbox.defaultChecked = checked;
52597         this.node.attributes.checked = checked;
52598         this.fireEvent('checkchange', this.node, checked);
52599     },
52600
52601     // private
52602     ecClick : function(e){
52603         if(!this.animating && this.node.isExpandable()){
52604             this.node.toggle();
52605         }
52606     },
52607
52608     // private
52609     startDrop : function(){
52610         this.dropping = true;
52611     },
52612
52613     // delayed drop so the click event doesn't get fired on a drop
52614     endDrop : function(){
52615        setTimeout(function(){
52616            this.dropping = false;
52617        }.createDelegate(this), 50);
52618     },
52619
52620     // private
52621     expand : function(){
52622         this.updateExpandIcon();
52623         this.ctNode.style.display = "";
52624     },
52625
52626     // private
52627     focus : function(){
52628         if(!this.node.preventHScroll){
52629             try{this.anchor.focus();
52630             }catch(e){}
52631         }else{
52632             try{
52633                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
52634                 var l = noscroll.scrollLeft;
52635                 this.anchor.focus();
52636                 noscroll.scrollLeft = l;
52637             }catch(e){}
52638         }
52639     },
52640
52641 /**
52642  * Sets the checked status of the tree node to the passed value, or, if no value was passed,
52643  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.
52644  * @param {Boolean} value (optional) The new checked status.
52645  */
52646     toggleCheck : function(value){
52647         var cb = this.checkbox;
52648         if(cb){
52649             cb.checked = (value === undefined ? !cb.checked : value);
52650             this.onCheckChange();
52651         }
52652     },
52653
52654     // private
52655     blur : function(){
52656         try{
52657             this.anchor.blur();
52658         }catch(e){}
52659     },
52660
52661     // private
52662     animExpand : function(callback){
52663         var ct = Ext.get(this.ctNode);
52664         ct.stopFx();
52665         if(!this.node.isExpandable()){
52666             this.updateExpandIcon();
52667             this.ctNode.style.display = "";
52668             Ext.callback(callback);
52669             return;
52670         }
52671         this.animating = true;
52672         this.updateExpandIcon();
52673
52674         ct.slideIn('t', {
52675            callback : function(){
52676                this.animating = false;
52677                Ext.callback(callback);
52678             },
52679             scope: this,
52680             duration: this.node.ownerTree.duration || .25
52681         });
52682     },
52683
52684     // private
52685     highlight : function(){
52686         var tree = this.node.getOwnerTree();
52687         Ext.fly(this.wrap).highlight(
52688             tree.hlColor || "C3DAF9",
52689             {endColor: tree.hlBaseColor}
52690         );
52691     },
52692
52693     // private
52694     collapse : function(){
52695         this.updateExpandIcon();
52696         this.ctNode.style.display = "none";
52697     },
52698
52699     // private
52700     animCollapse : function(callback){
52701         var ct = Ext.get(this.ctNode);
52702         ct.enableDisplayMode('block');
52703         ct.stopFx();
52704
52705         this.animating = true;
52706         this.updateExpandIcon();
52707
52708         ct.slideOut('t', {
52709             callback : function(){
52710                this.animating = false;
52711                Ext.callback(callback);
52712             },
52713             scope: this,
52714             duration: this.node.ownerTree.duration || .25
52715         });
52716     },
52717
52718     // private
52719     getContainer : function(){
52720         return this.ctNode;
52721     },
52722
52723 /**
52724  * Returns the element which encapsulates this node.
52725  * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.
52726  */
52727     getEl : function(){
52728         return this.wrap;
52729     },
52730
52731     // private
52732     appendDDGhost : function(ghostNode){
52733         ghostNode.appendChild(this.elNode.cloneNode(true));
52734     },
52735
52736     // private
52737     getDDRepairXY : function(){
52738         return Ext.lib.Dom.getXY(this.iconNode);
52739     },
52740
52741     // private
52742     onRender : function(){
52743         this.render();
52744     },
52745
52746     // private
52747     render : function(bulkRender){
52748         var n = this.node, a = n.attributes;
52749         var targetNode = n.parentNode ?
52750               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
52751
52752         if(!this.rendered){
52753             this.rendered = true;
52754
52755             this.renderElements(n, a, targetNode, bulkRender);
52756
52757             if(a.qtip){
52758                if(this.textNode.setAttributeNS){
52759                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
52760                    if(a.qtipTitle){
52761                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
52762                    }
52763                }else{
52764                    this.textNode.setAttribute("ext:qtip", a.qtip);
52765                    if(a.qtipTitle){
52766                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
52767                    }
52768                }
52769             }else if(a.qtipCfg){
52770                 a.qtipCfg.target = Ext.id(this.textNode);
52771                 Ext.QuickTips.register(a.qtipCfg);
52772             }
52773             this.initEvents();
52774             if(!this.node.expanded){
52775                 this.updateExpandIcon(true);
52776             }
52777         }else{
52778             if(bulkRender === true) {
52779                 targetNode.appendChild(this.wrap);
52780             }
52781         }
52782     },
52783
52784     // private
52785     renderElements : function(n, a, targetNode, bulkRender){
52786         // add some indent caching, this helps performance when rendering a large tree
52787         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
52788
52789         var cb = Ext.isBoolean(a.checked),
52790             nel,
52791             href = a.href ? a.href : Ext.isGecko ? "" : "#",
52792             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">',
52793             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
52794             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
52795             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
52796             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
52797             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
52798              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
52799             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
52800             "</li>"].join('');
52801
52802         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
52803             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
52804         }else{
52805             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
52806         }
52807
52808         this.elNode = this.wrap.childNodes[0];
52809         this.ctNode = this.wrap.childNodes[1];
52810         var cs = this.elNode.childNodes;
52811         this.indentNode = cs[0];
52812         this.ecNode = cs[1];
52813         this.iconNode = cs[2];
52814         var index = 3;
52815         if(cb){
52816             this.checkbox = cs[3];
52817             // fix for IE6
52818             this.checkbox.defaultChecked = this.checkbox.checked;
52819             index++;
52820         }
52821         this.anchor = cs[index];
52822         this.textNode = cs[index].firstChild;
52823     },
52824
52825 /**
52826  * Returns the &lt;a> element that provides focus for the node's UI.
52827  * @return {HtmlElement} The DOM anchor element.
52828  */
52829     getAnchor : function(){
52830         return this.anchor;
52831     },
52832
52833 /**
52834  * Returns the text node.
52835  * @return {HtmlNode} The DOM text node.
52836  */
52837     getTextEl : function(){
52838         return this.textNode;
52839     },
52840
52841 /**
52842  * Returns the icon &lt;img> element.
52843  * @return {HtmlElement} The DOM image element.
52844  */
52845     getIconEl : function(){
52846         return this.iconNode;
52847     },
52848
52849 /**
52850  * Returns the checked status of the node. If the node was rendered with no
52851  * checkbox, it returns false.
52852  * @return {Boolean} The checked flag.
52853  */
52854     isChecked : function(){
52855         return this.checkbox ? this.checkbox.checked : false;
52856     },
52857
52858     // private
52859     updateExpandIcon : function(){
52860         if(this.rendered){
52861             var n = this.node,
52862                 c1,
52863                 c2,
52864                 cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",
52865                 hasChild = n.hasChildNodes();
52866             if(hasChild || n.attributes.expandable){
52867                 if(n.expanded){
52868                     cls += "-minus";
52869                     c1 = "x-tree-node-collapsed";
52870                     c2 = "x-tree-node-expanded";
52871                 }else{
52872                     cls += "-plus";
52873                     c1 = "x-tree-node-expanded";
52874                     c2 = "x-tree-node-collapsed";
52875                 }
52876                 if(this.wasLeaf){
52877                     this.removeClass("x-tree-node-leaf");
52878                     this.wasLeaf = false;
52879                 }
52880                 if(this.c1 != c1 || this.c2 != c2){
52881                     Ext.fly(this.elNode).replaceClass(c1, c2);
52882                     this.c1 = c1; this.c2 = c2;
52883                 }
52884             }else{
52885                 if(!this.wasLeaf){
52886                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-collapsed");
52887                     delete this.c1;
52888                     delete this.c2;
52889                     this.wasLeaf = true;
52890                 }
52891             }
52892             var ecc = "x-tree-ec-icon "+cls;
52893             if(this.ecc != ecc){
52894                 this.ecNode.className = ecc;
52895                 this.ecc = ecc;
52896             }
52897         }
52898     },
52899
52900     // private
52901     onIdChange: function(id){
52902         if(this.rendered){
52903             this.elNode.setAttribute('ext:tree-node-id', id);
52904         }
52905     },
52906
52907     // private
52908     getChildIndent : function(){
52909         if(!this.childIndent){
52910             var buf = [],
52911                 p = this.node;
52912             while(p){
52913                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
52914                     if(!p.isLast()) {
52915                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
52916                     } else {
52917                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
52918                     }
52919                 }
52920                 p = p.parentNode;
52921             }
52922             this.childIndent = buf.join("");
52923         }
52924         return this.childIndent;
52925     },
52926
52927     // private
52928     renderIndent : function(){
52929         if(this.rendered){
52930             var indent = "",
52931                 p = this.node.parentNode;
52932             if(p){
52933                 indent = p.ui.getChildIndent();
52934             }
52935             if(this.indentMarkup != indent){ // don't rerender if not required
52936                 this.indentNode.innerHTML = indent;
52937                 this.indentMarkup = indent;
52938             }
52939             this.updateExpandIcon();
52940         }
52941     },
52942
52943     destroy : function(){
52944         if(this.elNode){
52945             Ext.dd.Registry.unregister(this.elNode.id);
52946         }
52947
52948         Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){
52949             if(this[el]){
52950                 Ext.fly(this[el]).remove();
52951                 delete this[el];
52952             }
52953         }, this);
52954         delete this.node;
52955     }
52956 };
52957
52958 /**
52959  * @class Ext.tree.RootTreeNodeUI
52960  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
52961  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
52962  * <p>
52963  * If you are customizing the Tree's user interface, you
52964  * may need to extend this class, but you should never need to instantiate this class.<br>
52965  */
52966 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
52967     // private
52968     render : function(){
52969         if(!this.rendered){
52970             var targetNode = this.node.ownerTree.innerCt.dom;
52971             this.node.expanded = true;
52972             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
52973             this.wrap = this.ctNode = targetNode.firstChild;
52974         }
52975     },
52976     collapse : Ext.emptyFn,
52977     expand : Ext.emptyFn
52978 });/**
52979  * @class Ext.tree.TreeLoader
52980  * @extends Ext.util.Observable
52981  * A TreeLoader provides for lazy loading of an {@link Ext.tree.TreeNode}'s child
52982  * nodes from a specified URL. The response must be a JavaScript Array definition
52983  * whose elements are node definition objects. e.g.:
52984  * <pre><code>
52985     [{
52986         id: 1,
52987         text: 'A leaf Node',
52988         leaf: true
52989     },{
52990         id: 2,
52991         text: 'A folder Node',
52992         children: [{
52993             id: 3,
52994             text: 'A child Node',
52995             leaf: true
52996         }]
52997    }]
52998 </code></pre>
52999  * <br><br>
53000  * A server request is sent, and child nodes are loaded only when a node is expanded.
53001  * The loading node's id is passed to the server under the parameter name "node" to
53002  * enable the server to produce the correct child nodes.
53003  * <br><br>
53004  * To pass extra parameters, an event handler may be attached to the "beforeload"
53005  * event, and the parameters specified in the TreeLoader's baseParams property:
53006  * <pre><code>
53007     myTreeLoader.on("beforeload", function(treeLoader, node) {
53008         this.baseParams.category = node.attributes.category;
53009     }, this);
53010 </code></pre>
53011  * This would pass an HTTP parameter called "category" to the server containing
53012  * the value of the Node's "category" attribute.
53013  * @constructor
53014  * Creates a new Treeloader.
53015  * @param {Object} config A config object containing config properties.
53016  */
53017 Ext.tree.TreeLoader = function(config){
53018     this.baseParams = {};
53019     Ext.apply(this, config);
53020
53021     this.addEvents(
53022         /**
53023          * @event beforeload
53024          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
53025          * @param {Object} This TreeLoader object.
53026          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
53027          * @param {Object} callback The callback function specified in the {@link #load} call.
53028          */
53029         "beforeload",
53030         /**
53031          * @event load
53032          * Fires when the node has been successfuly loaded.
53033          * @param {Object} This TreeLoader object.
53034          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
53035          * @param {Object} response The response object containing the data from the server.
53036          */
53037         "load",
53038         /**
53039          * @event loadexception
53040          * Fires if the network request failed.
53041          * @param {Object} This TreeLoader object.
53042          * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
53043          * @param {Object} response The response object containing the data from the server.
53044          */
53045         "loadexception"
53046     );
53047     Ext.tree.TreeLoader.superclass.constructor.call(this);
53048     if(Ext.isString(this.paramOrder)){
53049         this.paramOrder = this.paramOrder.split(/[\s,|]/);
53050     }
53051 };
53052
53053 Ext.extend(Ext.tree.TreeLoader, Ext.util.Observable, {
53054     /**
53055     * @cfg {String} dataUrl The URL from which to request a Json string which
53056     * specifies an array of node definition objects representing the child nodes
53057     * to be loaded.
53058     */
53059     /**
53060      * @cfg {String} requestMethod The HTTP request method for loading data (defaults to the value of {@link Ext.Ajax#method}).
53061      */
53062     /**
53063      * @cfg {String} url Equivalent to {@link #dataUrl}.
53064      */
53065     /**
53066      * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.
53067      */
53068     /**
53069     * @cfg {Object} baseParams (optional) An object containing properties which
53070     * specify HTTP parameters to be passed to each request for child nodes.
53071     */
53072     /**
53073     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
53074     * created by this loader. If the attributes sent by the server have an attribute in this object,
53075     * they take priority.
53076     */
53077     /**
53078     * @cfg {Object} uiProviders (optional) An object containing properties which
53079     * specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional
53080     * <i>uiProvider</i> attribute of a returned child node is a string rather
53081     * than a reference to a TreeNodeUI implementation, then that string value
53082     * is used as a property name in the uiProviders object.
53083     */
53084     uiProviders : {},
53085
53086     /**
53087     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
53088     * child nodes before loading.
53089     */
53090     clearOnLoad : true,
53091
53092     /**
53093      * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. Only used when using directFn.
53094      * Specifies the params in the order in which they must be passed to the server-side Direct method
53095      * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
53096      * comma, or pipe. For example,
53097      * any of the following would be acceptable:<pre><code>
53098 nodeParameter: 'node',
53099 paramOrder: ['param1','param2','param3']
53100 paramOrder: 'node param1 param2 param3'
53101 paramOrder: 'param1,node,param2,param3'
53102 paramOrder: 'param1|param2|param|node'
53103      </code></pre>
53104      */
53105     paramOrder: undefined,
53106
53107     /**
53108      * @cfg {Boolean} paramsAsHash Only used when using directFn.
53109      * Send parameters as a collection of named arguments (defaults to <tt>false</tt>). Providing a
53110      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
53111      */
53112     paramsAsHash: false,
53113
53114     /**
53115      * @cfg {String} nodeParameter The name of the parameter sent to the server which contains
53116      * the identifier of the node. Defaults to <tt>'node'</tt>.
53117      */
53118     nodeParameter: 'node',
53119
53120     /**
53121      * @cfg {Function} directFn
53122      * Function to call when executing a request.
53123      */
53124     directFn : undefined,
53125
53126     /**
53127      * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.
53128      * This is called automatically when a node is expanded, but may be used to reload
53129      * a node (or append new children if the {@link #clearOnLoad} option is false.)
53130      * @param {Ext.tree.TreeNode} node
53131      * @param {Function} callback Function to call after the node has been loaded. The
53132      * function is passed the TreeNode which was requested to be loaded.
53133      * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed.
53134      * defaults to the loaded TreeNode.
53135      */
53136     load : function(node, callback, scope){
53137         if(this.clearOnLoad){
53138             while(node.firstChild){
53139                 node.removeChild(node.firstChild);
53140             }
53141         }
53142         if(this.doPreload(node)){ // preloaded json children
53143             this.runCallback(callback, scope || node, [node]);
53144         }else if(this.directFn || this.dataUrl || this.url){
53145             this.requestData(node, callback, scope || node);
53146         }
53147     },
53148
53149     doPreload : function(node){
53150         if(node.attributes.children){
53151             if(node.childNodes.length < 1){ // preloaded?
53152                 var cs = node.attributes.children;
53153                 node.beginUpdate();
53154                 for(var i = 0, len = cs.length; i < len; i++){
53155                     var cn = node.appendChild(this.createNode(cs[i]));
53156                     if(this.preloadChildren){
53157                         this.doPreload(cn);
53158                     }
53159                 }
53160                 node.endUpdate();
53161             }
53162             return true;
53163         }
53164         return false;
53165     },
53166
53167     getParams: function(node){
53168         var bp = Ext.apply({}, this.baseParams),
53169             np = this.nodeParameter,
53170             po = this.paramOrder;
53171
53172         np && (bp[ np ] = node.id);
53173
53174         if(this.directFn){
53175             var buf = [node.id];
53176             if(po){
53177                 // reset 'buf' if the nodeParameter was included in paramOrder
53178                 if(np && po.indexOf(np) > -1){
53179                     buf = [];
53180                 }
53181
53182                 for(var i = 0, len = po.length; i < len; i++){
53183                     buf.push(bp[ po[i] ]);
53184                 }
53185             }else if(this.paramsAsHash){
53186                 buf = [bp];
53187             }
53188             return buf;
53189         }else{
53190             return bp;
53191         }
53192     },
53193
53194     requestData : function(node, callback, scope){
53195         if(this.fireEvent("beforeload", this, node, callback) !== false){
53196             if(this.directFn){
53197                 var args = this.getParams(node);
53198                 args.push(this.processDirectResponse.createDelegate(this, [{callback: callback, node: node, scope: scope}], true));
53199                 this.directFn.apply(window, args);
53200             }else{
53201                 this.transId = Ext.Ajax.request({
53202                     method:this.requestMethod,
53203                     url: this.dataUrl||this.url,
53204                     success: this.handleResponse,
53205                     failure: this.handleFailure,
53206                     scope: this,
53207                     argument: {callback: callback, node: node, scope: scope},
53208                     params: this.getParams(node)
53209                 });
53210             }
53211         }else{
53212             // if the load is cancelled, make sure we notify
53213             // the node that we are done
53214             this.runCallback(callback, scope || node, []);
53215         }
53216     },
53217
53218     processDirectResponse: function(result, response, args){
53219         if(response.status){
53220             this.handleResponse({
53221                 responseData: Ext.isArray(result) ? result : null,
53222                 responseText: result,
53223                 argument: args
53224             });
53225         }else{
53226             this.handleFailure({
53227                 argument: args
53228             });
53229         }
53230     },
53231
53232     // private
53233     runCallback: function(cb, scope, args){
53234         if(Ext.isFunction(cb)){
53235             cb.apply(scope, args);
53236         }
53237     },
53238
53239     isLoading : function(){
53240         return !!this.transId;
53241     },
53242
53243     abort : function(){
53244         if(this.isLoading()){
53245             Ext.Ajax.abort(this.transId);
53246         }
53247     },
53248
53249     /**
53250     * <p>Override this function for custom TreeNode node implementation, or to
53251     * modify the attributes at creation time.</p>
53252     * Example:<pre><code>
53253 new Ext.tree.TreePanel({
53254     ...
53255     loader: new Ext.tree.TreeLoader({
53256         url: 'dataUrl',
53257         createNode: function(attr) {
53258 //          Allow consolidation consignments to have
53259 //          consignments dropped into them.
53260             if (attr.isConsolidation) {
53261                 attr.iconCls = 'x-consol',
53262                 attr.allowDrop = true;
53263             }
53264             return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
53265         }
53266     }),
53267     ...
53268 });
53269 </code></pre>
53270     * @param attr {Object} The attributes from which to create the new node.
53271     */
53272     createNode : function(attr){
53273         // apply baseAttrs, nice idea Corey!
53274         if(this.baseAttrs){
53275             Ext.applyIf(attr, this.baseAttrs);
53276         }
53277         if(this.applyLoader !== false && !attr.loader){
53278             attr.loader = this;
53279         }
53280         if(Ext.isString(attr.uiProvider)){
53281            attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);
53282         }
53283         if(attr.nodeType){
53284             return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr);
53285         }else{
53286             return attr.leaf ?
53287                         new Ext.tree.TreeNode(attr) :
53288                         new Ext.tree.AsyncTreeNode(attr);
53289         }
53290     },
53291
53292     processResponse : function(response, node, callback, scope){
53293         var json = response.responseText;
53294         try {
53295             var o = response.responseData || Ext.decode(json);
53296             node.beginUpdate();
53297             for(var i = 0, len = o.length; i < len; i++){
53298                 var n = this.createNode(o[i]);
53299                 if(n){
53300                     node.appendChild(n);
53301                 }
53302             }
53303             node.endUpdate();
53304             this.runCallback(callback, scope || node, [node]);
53305         }catch(e){
53306             this.handleFailure(response);
53307         }
53308     },
53309
53310     handleResponse : function(response){
53311         this.transId = false;
53312         var a = response.argument;
53313         this.processResponse(response, a.node, a.callback, a.scope);
53314         this.fireEvent("load", this, a.node, response);
53315     },
53316
53317     handleFailure : function(response){
53318         this.transId = false;
53319         var a = response.argument;
53320         this.fireEvent("loadexception", this, a.node, response);
53321         this.runCallback(a.callback, a.scope || a.node, [a.node]);
53322     },
53323
53324     destroy : function(){
53325         this.abort();
53326         this.purgeListeners();
53327     }
53328 });/**
53329  * @class Ext.tree.TreeFilter
53330  * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
53331  * @param {TreePanel} tree
53332  * @param {Object} config (optional)
53333  */
53334 Ext.tree.TreeFilter = function(tree, config){
53335     this.tree = tree;
53336     this.filtered = {};
53337     Ext.apply(this, config);
53338 };
53339
53340 Ext.tree.TreeFilter.prototype = {
53341     clearBlank:false,
53342     reverse:false,
53343     autoClear:false,
53344     remove:false,
53345
53346      /**
53347      * Filter the data by a specific attribute.
53348      * @param {String/RegExp} value Either string that the attribute value
53349      * should start with or a RegExp to test against the attribute
53350      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
53351      * @param {TreeNode} startNode (optional) The node to start the filter at.
53352      */
53353     filter : function(value, attr, startNode){
53354         attr = attr || "text";
53355         var f;
53356         if(typeof value == "string"){
53357             var vlen = value.length;
53358             // auto clear empty filter
53359             if(vlen == 0 && this.clearBlank){
53360                 this.clear();
53361                 return;
53362             }
53363             value = value.toLowerCase();
53364             f = function(n){
53365                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
53366             };
53367         }else if(value.exec){ // regex?
53368             f = function(n){
53369                 return value.test(n.attributes[attr]);
53370             };
53371         }else{
53372             throw 'Illegal filter type, must be string or regex';
53373         }
53374         this.filterBy(f, null, startNode);
53375         },
53376
53377     /**
53378      * Filter by a function. The passed function will be called with each
53379      * node in the tree (or from the startNode). If the function returns true, the node is kept
53380      * otherwise it is filtered. If a node is filtered, its children are also filtered.
53381      * @param {Function} fn The filter function
53382      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
53383      */
53384     filterBy : function(fn, scope, startNode){
53385         startNode = startNode || this.tree.root;
53386         if(this.autoClear){
53387             this.clear();
53388         }
53389         var af = this.filtered, rv = this.reverse;
53390         var f = function(n){
53391             if(n == startNode){
53392                 return true;
53393             }
53394             if(af[n.id]){
53395                 return false;
53396             }
53397             var m = fn.call(scope || n, n);
53398             if(!m || rv){
53399                 af[n.id] = n;
53400                 n.ui.hide();
53401                 return false;
53402             }
53403             return true;
53404         };
53405         startNode.cascade(f);
53406         if(this.remove){
53407            for(var id in af){
53408                if(typeof id != "function"){
53409                    var n = af[id];
53410                    if(n && n.parentNode){
53411                        n.parentNode.removeChild(n);
53412                    }
53413                }
53414            }
53415         }
53416     },
53417
53418     /**
53419      * Clears the current filter. Note: with the "remove" option
53420      * set a filter cannot be cleared.
53421      */
53422     clear : function(){
53423         var t = this.tree;
53424         var af = this.filtered;
53425         for(var id in af){
53426             if(typeof id != "function"){
53427                 var n = af[id];
53428                 if(n){
53429                     n.ui.show();
53430                 }
53431             }
53432         }
53433         this.filtered = {};
53434     }
53435 };
53436 /**
53437  * @class Ext.tree.TreeSorter
53438  * Provides sorting of nodes in a {@link Ext.tree.TreePanel}.  The TreeSorter automatically monitors events on the
53439  * associated TreePanel that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange).
53440  * Example usage:<br />
53441  * <pre><code>
53442 new Ext.tree.TreeSorter(myTree, {
53443     folderSort: true,
53444     dir: "desc",
53445     sortType: function(node) {
53446         // sort by a custom, typed attribute:
53447         return parseInt(node.id, 10);
53448     }
53449 });
53450 </code></pre>
53451  * @constructor
53452  * @param {TreePanel} tree
53453  * @param {Object} config
53454  */
53455 Ext.tree.TreeSorter = function(tree, config){
53456     /**
53457      * @cfg {Boolean} folderSort True to sort leaf nodes under non-leaf nodes (defaults to false)
53458      */
53459     /**
53460      * @cfg {String} property The named attribute on the node to sort by (defaults to "text").  Note that this
53461      * property is only used if no {@link #sortType} function is specified, otherwise it is ignored.
53462      */
53463     /**
53464      * @cfg {String} dir The direction to sort ("asc" or "desc," case-insensitive, defaults to "asc")
53465      */
53466     /**
53467      * @cfg {String} leafAttr The attribute used to determine leaf nodes when {@link #folderSort} = true (defaults to "leaf")
53468      */
53469     /**
53470      * @cfg {Boolean} caseSensitive true for case-sensitive sort (defaults to false)
53471      */
53472     /**
53473      * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting.  The function
53474      * will be called with a single parameter (the {@link Ext.tree.TreeNode} being evaluated) and is expected to return
53475      * the node's sort value cast to the specific data type required for sorting.  This could be used, for example, when
53476      * a node's text (or other attribute) should be sorted as a date or numeric value.  See the class description for
53477      * example usage.  Note that if a sortType is specified, any {@link #property} config will be ignored.
53478      */
53479
53480     Ext.apply(this, config);
53481     tree.on("beforechildrenrendered", this.doSort, this);
53482     tree.on("append", this.updateSort, this);
53483     tree.on("insert", this.updateSort, this);
53484     tree.on("textchange", this.updateSortParent, this);
53485
53486     var dsc = this.dir && this.dir.toLowerCase() == "desc";
53487     var p = this.property || "text";
53488     var sortType = this.sortType;
53489     var fs = this.folderSort;
53490     var cs = this.caseSensitive === true;
53491     var leafAttr = this.leafAttr || 'leaf';
53492
53493     this.sortFn = function(n1, n2){
53494         if(fs){
53495             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
53496                 return 1;
53497             }
53498             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
53499                 return -1;
53500             }
53501         }
53502         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
53503         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
53504         if(v1 < v2){
53505             return dsc ? +1 : -1;
53506         }else if(v1 > v2){
53507             return dsc ? -1 : +1;
53508         }else{
53509             return 0;
53510         }
53511     };
53512 };
53513
53514 Ext.tree.TreeSorter.prototype = {
53515     doSort : function(node){
53516         node.sort(this.sortFn);
53517     },
53518
53519     compareNodes : function(n1, n2){
53520         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
53521     },
53522
53523     updateSort : function(tree, node){
53524         if(node.childrenRendered){
53525             this.doSort.defer(1, this, [node]);
53526         }
53527     },
53528
53529     updateSortParent : function(node){
53530         var p = node.parentNode;
53531         if(p && p.childrenRendered){
53532             this.doSort.defer(1, this, [p]);
53533         }
53534     }
53535 };/**
53536  * @class Ext.tree.TreeDropZone
53537  * @extends Ext.dd.DropZone
53538  * @constructor
53539  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping
53540  * @param {Object} config
53541  */
53542 if(Ext.dd.DropZone){
53543     
53544 Ext.tree.TreeDropZone = function(tree, config){
53545     /**
53546      * @cfg {Boolean} allowParentInsert
53547      * Allow inserting a dragged node between an expanded parent node and its first child that will become a
53548      * sibling of the parent when dropped (defaults to false)
53549      */
53550     this.allowParentInsert = config.allowParentInsert || false;
53551     /**
53552      * @cfg {String} allowContainerDrop
53553      * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)
53554      */
53555     this.allowContainerDrop = config.allowContainerDrop || false;
53556     /**
53557      * @cfg {String} appendOnly
53558      * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)
53559      */
53560     this.appendOnly = config.appendOnly || false;
53561
53562     Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);
53563     /**
53564     * The TreePanel for this drop zone
53565     * @type Ext.tree.TreePanel
53566     * @property
53567     */
53568     this.tree = tree;
53569     /**
53570     * Arbitrary data that can be associated with this tree and will be included in the event object that gets
53571     * passed to any nodedragover event handler (defaults to {})
53572     * @type Ext.tree.TreePanel
53573     * @property
53574     */
53575     this.dragOverData = {};
53576     // private
53577     this.lastInsertClass = "x-tree-no-status";
53578 };
53579
53580 Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {
53581     /**
53582      * @cfg {String} ddGroup
53583      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
53584      * interact with other drag drop objects in the same group (defaults to 'TreeDD').
53585      */
53586     ddGroup : "TreeDD",
53587
53588     /**
53589      * @cfg {String} expandDelay
53590      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node
53591      * over the target (defaults to 1000)
53592      */
53593     expandDelay : 1000,
53594
53595     // private
53596     expandNode : function(node){
53597         if(node.hasChildNodes() && !node.isExpanded()){
53598             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
53599         }
53600     },
53601
53602     // private
53603     queueExpand : function(node){
53604         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
53605     },
53606
53607     // private
53608     cancelExpand : function(){
53609         if(this.expandProcId){
53610             clearTimeout(this.expandProcId);
53611             this.expandProcId = false;
53612         }
53613     },
53614
53615     // private
53616     isValidDropPoint : function(n, pt, dd, e, data){
53617         if(!n || !data){ return false; }
53618         var targetNode = n.node;
53619         var dropNode = data.node;
53620         // default drop rules
53621         if(!(targetNode && targetNode.isTarget && pt)){
53622             return false;
53623         }
53624         if(pt == "append" && targetNode.allowChildren === false){
53625             return false;
53626         }
53627         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
53628             return false;
53629         }
53630         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
53631             return false;
53632         }
53633         // reuse the object
53634         var overEvent = this.dragOverData;
53635         overEvent.tree = this.tree;
53636         overEvent.target = targetNode;
53637         overEvent.data = data;
53638         overEvent.point = pt;
53639         overEvent.source = dd;
53640         overEvent.rawEvent = e;
53641         overEvent.dropNode = dropNode;
53642         overEvent.cancel = false;  
53643         var result = this.tree.fireEvent("nodedragover", overEvent);
53644         return overEvent.cancel === false && result !== false;
53645     },
53646
53647     // private
53648     getDropPoint : function(e, n, dd){
53649         var tn = n.node;
53650         if(tn.isRoot){
53651             return tn.allowChildren !== false ? "append" : false; // always append for root
53652         }
53653         var dragEl = n.ddel;
53654         var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
53655         var y = Ext.lib.Event.getPageY(e);
53656         var noAppend = tn.allowChildren === false || tn.isLeaf();
53657         if(this.appendOnly || tn.parentNode.allowChildren === false){
53658             return noAppend ? false : "append";
53659         }
53660         var noBelow = false;
53661         if(!this.allowParentInsert){
53662             noBelow = tn.hasChildNodes() && tn.isExpanded();
53663         }
53664         var q = (b - t) / (noAppend ? 2 : 3);
53665         if(y >= t && y < (t + q)){
53666             return "above";
53667         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
53668             return "below";
53669         }else{
53670             return "append";
53671         }
53672     },
53673
53674     // private
53675     onNodeEnter : function(n, dd, e, data){
53676         this.cancelExpand();
53677     },
53678     
53679     onContainerOver : function(dd, e, data) {
53680         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
53681             return this.dropAllowed;
53682         }
53683         return this.dropNotAllowed;
53684     },
53685
53686     // private
53687     onNodeOver : function(n, dd, e, data){
53688         var pt = this.getDropPoint(e, n, dd);
53689         var node = n.node;
53690         
53691         // auto node expand check
53692         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
53693             this.queueExpand(node);
53694         }else if(pt != "append"){
53695             this.cancelExpand();
53696         }
53697         
53698         // set the insert point style on the target node
53699         var returnCls = this.dropNotAllowed;
53700         if(this.isValidDropPoint(n, pt, dd, e, data)){
53701            if(pt){
53702                var el = n.ddel;
53703                var cls;
53704                if(pt == "above"){
53705                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
53706                    cls = "x-tree-drag-insert-above";
53707                }else if(pt == "below"){
53708                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
53709                    cls = "x-tree-drag-insert-below";
53710                }else{
53711                    returnCls = "x-tree-drop-ok-append";
53712                    cls = "x-tree-drag-append";
53713                }
53714                if(this.lastInsertClass != cls){
53715                    Ext.fly(el).replaceClass(this.lastInsertClass, cls);
53716                    this.lastInsertClass = cls;
53717                }
53718            }
53719        }
53720        return returnCls;
53721     },
53722
53723     // private
53724     onNodeOut : function(n, dd, e, data){
53725         this.cancelExpand();
53726         this.removeDropIndicators(n);
53727     },
53728
53729     // private
53730     onNodeDrop : function(n, dd, e, data){
53731         var point = this.getDropPoint(e, n, dd);
53732         var targetNode = n.node;
53733         targetNode.ui.startDrop();
53734         if(!this.isValidDropPoint(n, point, dd, e, data)){
53735             targetNode.ui.endDrop();
53736             return false;
53737         }
53738         // first try to find the drop node
53739         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
53740         return this.processDrop(targetNode, data, point, dd, e, dropNode);
53741     },
53742     
53743     onContainerDrop : function(dd, e, data){
53744         if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) {
53745             var targetNode = this.tree.getRootNode();       
53746             targetNode.ui.startDrop();
53747             var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null);
53748             return this.processDrop(targetNode, data, 'append', dd, e, dropNode);
53749         }
53750         return false;
53751     },
53752     
53753     // private
53754     processDrop: function(target, data, point, dd, e, dropNode){
53755         var dropEvent = {
53756             tree : this.tree,
53757             target: target,
53758             data: data,
53759             point: point,
53760             source: dd,
53761             rawEvent: e,
53762             dropNode: dropNode,
53763             cancel: !dropNode,
53764             dropStatus: false
53765         };
53766         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
53767         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
53768             target.ui.endDrop();
53769             return dropEvent.dropStatus;
53770         }
53771     
53772         target = dropEvent.target;
53773         if(point == 'append' && !target.isExpanded()){
53774             target.expand(false, null, function(){
53775                 this.completeDrop(dropEvent);
53776             }.createDelegate(this));
53777         }else{
53778             this.completeDrop(dropEvent);
53779         }
53780         return true;
53781     },
53782
53783     // private
53784     completeDrop : function(de){
53785         var ns = de.dropNode, p = de.point, t = de.target;
53786         if(!Ext.isArray(ns)){
53787             ns = [ns];
53788         }
53789         var n;
53790         for(var i = 0, len = ns.length; i < len; i++){
53791             n = ns[i];
53792             if(p == "above"){
53793                 t.parentNode.insertBefore(n, t);
53794             }else if(p == "below"){
53795                 t.parentNode.insertBefore(n, t.nextSibling);
53796             }else{
53797                 t.appendChild(n);
53798             }
53799         }
53800         n.ui.focus();
53801         if(Ext.enableFx && this.tree.hlDrop){
53802             n.ui.highlight();
53803         }
53804         t.ui.endDrop();
53805         this.tree.fireEvent("nodedrop", de);
53806     },
53807
53808     // private
53809     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
53810         if(Ext.enableFx && this.tree.hlDrop){
53811             dropNode.ui.focus();
53812             dropNode.ui.highlight();
53813         }
53814         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
53815     },
53816
53817     // private
53818     getTree : function(){
53819         return this.tree;
53820     },
53821
53822     // private
53823     removeDropIndicators : function(n){
53824         if(n && n.ddel){
53825             var el = n.ddel;
53826             Ext.fly(el).removeClass([
53827                     "x-tree-drag-insert-above",
53828                     "x-tree-drag-insert-below",
53829                     "x-tree-drag-append"]);
53830             this.lastInsertClass = "_noclass";
53831         }
53832     },
53833
53834     // private
53835     beforeDragDrop : function(target, e, id){
53836         this.cancelExpand();
53837         return true;
53838     },
53839
53840     // private
53841     afterRepair : function(data){
53842         if(data && Ext.enableFx){
53843             data.node.ui.highlight();
53844         }
53845         this.hideProxy();
53846     }    
53847 });
53848
53849 }/**
53850  * @class Ext.tree.TreeDragZone
53851  * @extends Ext.dd.DragZone
53852  * @constructor
53853  * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dragging
53854  * @param {Object} config
53855  */
53856 if(Ext.dd.DragZone){
53857 Ext.tree.TreeDragZone = function(tree, config){
53858     Ext.tree.TreeDragZone.superclass.constructor.call(this, tree.innerCt, config);
53859     /**
53860     * The TreePanel for this drag zone
53861     * @type Ext.tree.TreePanel
53862     * @property
53863     */
53864     this.tree = tree;
53865 };
53866
53867 Ext.extend(Ext.tree.TreeDragZone, Ext.dd.DragZone, {
53868     /**
53869      * @cfg {String} ddGroup
53870      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
53871      * interact with other drag drop objects in the same group (defaults to 'TreeDD').
53872      */
53873     ddGroup : "TreeDD",
53874
53875     // private
53876     onBeforeDrag : function(data, e){
53877         var n = data.node;
53878         return n && n.draggable && !n.disabled;
53879     },
53880
53881     // private
53882     onInitDrag : function(e){
53883         var data = this.dragData;
53884         this.tree.getSelectionModel().select(data.node);
53885         this.tree.eventModel.disable();
53886         this.proxy.update("");
53887         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
53888         this.tree.fireEvent("startdrag", this.tree, data.node, e);
53889     },
53890
53891     // private
53892     getRepairXY : function(e, data){
53893         return data.node.ui.getDDRepairXY();
53894     },
53895
53896     // private
53897     onEndDrag : function(data, e){
53898         this.tree.eventModel.enable.defer(100, this.tree.eventModel);
53899         this.tree.fireEvent("enddrag", this.tree, data.node, e);
53900     },
53901
53902     // private
53903     onValidDrop : function(dd, e, id){
53904         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
53905         this.hideProxy();
53906     },
53907
53908     // private
53909     beforeInvalidDrop : function(e, id){
53910         // this scrolls the original position back into view
53911         var sm = this.tree.getSelectionModel();
53912         sm.clearSelections();
53913         sm.select(this.dragData.node);
53914     },
53915     
53916     // private
53917     afterRepair : function(){
53918         if (Ext.enableFx && this.tree.hlDrop) {
53919             Ext.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
53920         }
53921         this.dragging = false;
53922     }
53923 });
53924 }/**
53925  * @class Ext.tree.TreeEditor
53926  * @extends Ext.Editor
53927  * Provides editor functionality for inline tree node editing.  Any valid {@link Ext.form.Field} subclass can be used
53928  * as the editor field.
53929  * @constructor
53930  * @param {TreePanel} tree
53931  * @param {Object} fieldConfig (optional) Either a prebuilt {@link Ext.form.Field} instance or a Field config object
53932  * that will be applied to the default field instance (defaults to a {@link Ext.form.TextField}).
53933  * @param {Object} config (optional) A TreeEditor config object
53934  */
53935 Ext.tree.TreeEditor = function(tree, fc, config){
53936     fc = fc || {};
53937     var field = fc.events ? fc : new Ext.form.TextField(fc);
53938     
53939     Ext.tree.TreeEditor.superclass.constructor.call(this, field, config);
53940
53941     this.tree = tree;
53942
53943     if(!tree.rendered){
53944         tree.on('render', this.initEditor, this);
53945     }else{
53946         this.initEditor(tree);
53947     }
53948 };
53949
53950 Ext.extend(Ext.tree.TreeEditor, Ext.Editor, {
53951     /**
53952      * @cfg {String} alignment
53953      * The position to align to (see {@link Ext.Element#alignTo} for more details, defaults to "l-l").
53954      */
53955     alignment: "l-l",
53956     // inherit
53957     autoSize: false,
53958     /**
53959      * @cfg {Boolean} hideEl
53960      * True to hide the bound element while the editor is displayed (defaults to false)
53961      */
53962     hideEl : false,
53963     /**
53964      * @cfg {String} cls
53965      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
53966      */
53967     cls: "x-small-editor x-tree-editor",
53968     /**
53969      * @cfg {Boolean} shim
53970      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
53971      */
53972     shim:false,
53973     // inherit
53974     shadow:"frame",
53975     /**
53976      * @cfg {Number} maxWidth
53977      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
53978      * the containing tree element's size, it will be automatically limited for you to the container width, taking
53979      * scroll and client offsets into account prior to each edit.
53980      */
53981     maxWidth: 250,
53982     /**
53983      * @cfg {Number} editDelay The number of milliseconds between clicks to register a double-click that will trigger
53984      * editing on the current node (defaults to 350).  If two clicks occur on the same node within this time span,
53985      * the editor for the node will display, otherwise it will be processed as a regular click.
53986      */
53987     editDelay : 350,
53988
53989     initEditor : function(tree){
53990         tree.on({
53991             scope      : this,
53992             beforeclick: this.beforeNodeClick,
53993             dblclick   : this.onNodeDblClick
53994         });
53995         
53996         this.on({
53997             scope          : this,
53998             complete       : this.updateNode,
53999             beforestartedit: this.fitToTree,
54000             specialkey     : this.onSpecialKey
54001         });
54002         
54003         this.on('startedit', this.bindScroll, this, {delay:10});
54004     },
54005
54006     // private
54007     fitToTree : function(ed, el){
54008         var td = this.tree.getTreeEl().dom, nd = el.dom;
54009         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
54010             td.scrollLeft = nd.offsetLeft;
54011         }
54012         var w = Math.min(
54013                 this.maxWidth,
54014                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
54015         this.setSize(w, '');
54016     },
54017
54018     /**
54019      * Edit the text of the passed {@link Ext.tree.TreeNode TreeNode}.
54020      * @param node {Ext.tree.TreeNode} The TreeNode to edit. The TreeNode must be {@link Ext.tree.TreeNode#editable editable}.
54021      */
54022     triggerEdit : function(node, defer){
54023         this.completeEdit();
54024                 if(node.attributes.editable !== false){
54025            /**
54026             * The {@link Ext.tree.TreeNode TreeNode} this editor is bound to. Read-only.
54027             * @type Ext.tree.TreeNode
54028             * @property editNode
54029             */
54030                         this.editNode = node;
54031             if(this.tree.autoScroll){
54032                 Ext.fly(node.ui.getEl()).scrollIntoView(this.tree.body);
54033             }
54034             var value = node.text || '';
54035             if (!Ext.isGecko && Ext.isEmpty(node.text)){
54036                 node.setText('&#160;');
54037             }
54038             this.autoEditTimer = this.startEdit.defer(this.editDelay, this, [node.ui.textNode, value]);
54039             return false;
54040         }
54041     },
54042
54043     // private
54044     bindScroll : function(){
54045         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
54046     },
54047
54048     // private
54049     beforeNodeClick : function(node, e){
54050         clearTimeout(this.autoEditTimer);
54051         if(this.tree.getSelectionModel().isSelected(node)){
54052             e.stopEvent();
54053             return this.triggerEdit(node);
54054         }
54055     },
54056
54057     onNodeDblClick : function(node, e){
54058         clearTimeout(this.autoEditTimer);
54059     },
54060
54061     // private
54062     updateNode : function(ed, value){
54063         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
54064         this.editNode.setText(value);
54065     },
54066
54067     // private
54068     onHide : function(){
54069         Ext.tree.TreeEditor.superclass.onHide.call(this);
54070         if(this.editNode){
54071             this.editNode.ui.focus.defer(50, this.editNode.ui);
54072         }
54073     },
54074
54075     // private
54076     onSpecialKey : function(field, e){
54077         var k = e.getKey();
54078         if(k == e.ESC){
54079             e.stopEvent();
54080             this.cancelEdit();
54081         }else if(k == e.ENTER && !e.hasModifier()){
54082             e.stopEvent();
54083             this.completeEdit();
54084         }
54085     },
54086     
54087     onDestroy : function(){
54088         clearTimeout(this.autoEditTimer);
54089         Ext.tree.TreeEditor.superclass.onDestroy.call(this);
54090         var tree = this.tree;
54091         tree.un('beforeclick', this.beforeNodeClick, this);
54092         tree.un('dblclick', this.onNodeDblClick, this);
54093     }
54094 });/*! SWFObject v2.2 <http://code.google.com/p/swfobject/> 
54095     is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
54096 */
54097
54098 var swfobject = function() {
54099     
54100     var UNDEF = "undefined",
54101         OBJECT = "object",
54102         SHOCKWAVE_FLASH = "Shockwave Flash",
54103         SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
54104         FLASH_MIME_TYPE = "application/x-shockwave-flash",
54105         EXPRESS_INSTALL_ID = "SWFObjectExprInst",
54106         ON_READY_STATE_CHANGE = "onreadystatechange",
54107         
54108         win = window,
54109         doc = document,
54110         nav = navigator,
54111         
54112         plugin = false,
54113         domLoadFnArr = [main],
54114         regObjArr = [],
54115         objIdArr = [],
54116         listenersArr = [],
54117         storedAltContent,
54118         storedAltContentId,
54119         storedCallbackFn,
54120         storedCallbackObj,
54121         isDomLoaded = false,
54122         isExpressInstallActive = false,
54123         dynamicStylesheet,
54124         dynamicStylesheetMedia,
54125         autoHideShow = true,
54126     
54127     /* Centralized function for browser feature detection
54128         - User agent string detection is only used when no good alternative is possible
54129         - Is executed directly for optimal performance
54130     */  
54131     ua = function() {
54132         var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
54133             u = nav.userAgent.toLowerCase(),
54134             p = nav.platform.toLowerCase(),
54135             windows = p ? /win/.test(p) : /win/.test(u),
54136             mac = p ? /mac/.test(p) : /mac/.test(u),
54137             webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
54138             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
54139             playerVersion = [0,0,0],
54140             d = null;
54141         if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
54142             d = nav.plugins[SHOCKWAVE_FLASH].description;
54143             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+
54144                 plugin = true;
54145                 ie = false; // cascaded feature detection for Internet Explorer
54146                 d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
54147                 playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
54148                 playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
54149                 playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
54150             }
54151         }
54152         else if (typeof win.ActiveXObject != UNDEF) {
54153             try {
54154                 var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
54155                 if (a) { // a will return null when ActiveX is disabled
54156                     d = a.GetVariable("$version");
54157                     if (d) {
54158                         ie = true; // cascaded feature detection for Internet Explorer
54159                         d = d.split(" ")[1].split(",");
54160                         playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
54161                     }
54162                 }
54163             }
54164             catch(e) {}
54165         }
54166         return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
54167     }(),
54168     
54169     /* Cross-browser onDomLoad
54170         - Will fire an event as soon as the DOM of a web page is loaded
54171         - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
54172         - Regular onload serves as fallback
54173     */ 
54174     onDomLoad = function() {
54175         if (!ua.w3) { return; }
54176         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 
54177             callDomLoadFunctions();
54178         }
54179         if (!isDomLoaded) {
54180             if (typeof doc.addEventListener != UNDEF) {
54181                 doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
54182             }       
54183             if (ua.ie && ua.win) {
54184                 doc.attachEvent(ON_READY_STATE_CHANGE, function() {
54185                     if (doc.readyState == "complete") {
54186                         doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
54187                         callDomLoadFunctions();
54188                     }
54189                 });
54190                 if (win == top) { // if not inside an iframe
54191                     (function(){
54192                         if (isDomLoaded) { return; }
54193                         try {
54194                             doc.documentElement.doScroll("left");
54195                         }
54196                         catch(e) {
54197                             setTimeout(arguments.callee, 0);
54198                             return;
54199                         }
54200                         callDomLoadFunctions();
54201                     })();
54202                 }
54203             }
54204             if (ua.wk) {
54205                 (function(){
54206                     if (isDomLoaded) { return; }
54207                     if (!/loaded|complete/.test(doc.readyState)) {
54208                         setTimeout(arguments.callee, 0);
54209                         return;
54210                     }
54211                     callDomLoadFunctions();
54212                 })();
54213             }
54214             addLoadEvent(callDomLoadFunctions);
54215         }
54216     }();
54217     
54218     function callDomLoadFunctions() {
54219         if (isDomLoaded) { return; }
54220         try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
54221             var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
54222             t.parentNode.removeChild(t);
54223         }
54224         catch (e) { return; }
54225         isDomLoaded = true;
54226         var dl = domLoadFnArr.length;
54227         for (var i = 0; i < dl; i++) {
54228             domLoadFnArr[i]();
54229         }
54230     }
54231     
54232     function addDomLoadEvent(fn) {
54233         if (isDomLoaded) {
54234             fn();
54235         }
54236         else { 
54237             domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
54238         }
54239     }
54240     
54241     /* Cross-browser onload
54242         - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
54243         - Will fire an event as soon as a web page including all of its assets are loaded 
54244      */
54245     function addLoadEvent(fn) {
54246         if (typeof win.addEventListener != UNDEF) {
54247             win.addEventListener("load", fn, false);
54248         }
54249         else if (typeof doc.addEventListener != UNDEF) {
54250             doc.addEventListener("load", fn, false);
54251         }
54252         else if (typeof win.attachEvent != UNDEF) {
54253             addListener(win, "onload", fn);
54254         }
54255         else if (typeof win.onload == "function") {
54256             var fnOld = win.onload;
54257             win.onload = function() {
54258                 fnOld();
54259                 fn();
54260             };
54261         }
54262         else {
54263             win.onload = fn;
54264         }
54265     }
54266     
54267     /* Main function
54268         - Will preferably execute onDomLoad, otherwise onload (as a fallback)
54269     */
54270     function main() { 
54271         if (plugin) {
54272             testPlayerVersion();
54273         }
54274         else {
54275             matchVersions();
54276         }
54277     }
54278     
54279     /* Detect the Flash Player version for non-Internet Explorer browsers
54280         - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
54281           a. Both release and build numbers can be detected
54282           b. Avoid wrong descriptions by corrupt installers provided by Adobe
54283           c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
54284         - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
54285     */
54286     function testPlayerVersion() {
54287         var b = doc.getElementsByTagName("body")[0];
54288         var o = createElement(OBJECT);
54289         o.setAttribute("type", FLASH_MIME_TYPE);
54290         var t = b.appendChild(o);
54291         if (t) {
54292             var counter = 0;
54293             (function(){
54294                 if (typeof t.GetVariable != UNDEF) {
54295                     var d = t.GetVariable("$version");
54296                     if (d) {
54297                         d = d.split(" ")[1].split(",");
54298                         ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
54299                     }
54300                 }
54301                 else if (counter < 10) {
54302                     counter++;
54303                     setTimeout(arguments.callee, 10);
54304                     return;
54305                 }
54306                 b.removeChild(o);
54307                 t = null;
54308                 matchVersions();
54309             })();
54310         }
54311         else {
54312             matchVersions();
54313         }
54314     }
54315     
54316     /* Perform Flash Player and SWF version matching; static publishing only
54317     */
54318     function matchVersions() {
54319         var rl = regObjArr.length;
54320         if (rl > 0) {
54321             for (var i = 0; i < rl; i++) { // for each registered object element
54322                 var id = regObjArr[i].id;
54323                 var cb = regObjArr[i].callbackFn;
54324                 var cbObj = {success:false, id:id};
54325                 if (ua.pv[0] > 0) {
54326                     var obj = getElementById(id);
54327                     if (obj) {
54328                         if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
54329                             setVisibility(id, true);
54330                             if (cb) {
54331                                 cbObj.success = true;
54332                                 cbObj.ref = getObjectById(id);
54333                                 cb(cbObj);
54334                             }
54335                         }
54336                         else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
54337                             var att = {};
54338                             att.data = regObjArr[i].expressInstall;
54339                             att.width = obj.getAttribute("width") || "0";
54340                             att.height = obj.getAttribute("height") || "0";
54341                             if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
54342                             if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
54343                             // parse HTML object param element's name-value pairs
54344                             var par = {};
54345                             var p = obj.getElementsByTagName("param");
54346                             var pl = p.length;
54347                             for (var j = 0; j < pl; j++) {
54348                                 if (p[j].getAttribute("name").toLowerCase() != "movie") {
54349                                     par[p[j].getAttribute("name")] = p[j].getAttribute("value");
54350                                 }
54351                             }
54352                             showExpressInstall(att, par, id, cb);
54353                         }
54354                         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
54355                             displayAltContent(obj);
54356                             if (cb) { cb(cbObj); }
54357                         }
54358                     }
54359                 }
54360                 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)
54361                     setVisibility(id, true);
54362                     if (cb) {
54363                         var o = getObjectById(id); // test whether there is an HTML object element or not
54364                         if (o && typeof o.SetVariable != UNDEF) { 
54365                             cbObj.success = true;
54366                             cbObj.ref = o;
54367                         }
54368                         cb(cbObj);
54369                     }
54370                 }
54371             }
54372         }
54373     }
54374     
54375     function getObjectById(objectIdStr) {
54376         var r = null;
54377         var o = getElementById(objectIdStr);
54378         if (o && o.nodeName == "OBJECT") {
54379             if (typeof o.SetVariable != UNDEF) {
54380                 r = o;
54381             }
54382             else {
54383                 var n = o.getElementsByTagName(OBJECT)[0];
54384                 if (n) {
54385                     r = n;
54386                 }
54387             }
54388         }
54389         return r;
54390     }
54391     
54392     /* Requirements for Adobe Express Install
54393         - only one instance can be active at a time
54394         - fp 6.0.65 or higher
54395         - Win/Mac OS only
54396         - no Webkit engines older than version 312
54397     */
54398     function canExpressInstall() {
54399         return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
54400     }
54401     
54402     /* Show the Adobe Express Install dialog
54403         - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
54404     */
54405     function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
54406         isExpressInstallActive = true;
54407         storedCallbackFn = callbackFn || null;
54408         storedCallbackObj = {success:false, id:replaceElemIdStr};
54409         var obj = getElementById(replaceElemIdStr);
54410         if (obj) {
54411             if (obj.nodeName == "OBJECT") { // static publishing
54412                 storedAltContent = abstractAltContent(obj);
54413                 storedAltContentId = null;
54414             }
54415             else { // dynamic publishing
54416                 storedAltContent = obj;
54417                 storedAltContentId = replaceElemIdStr;
54418             }
54419             att.id = EXPRESS_INSTALL_ID;
54420             if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
54421             if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
54422             doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
54423             var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
54424                 fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
54425             if (typeof par.flashvars != UNDEF) {
54426                 par.flashvars += "&" + fv;
54427             }
54428             else {
54429                 par.flashvars = fv;
54430             }
54431             // 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,
54432             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
54433             if (ua.ie && ua.win && obj.readyState != 4) {
54434                 var newObj = createElement("div");
54435                 replaceElemIdStr += "SWFObjectNew";
54436                 newObj.setAttribute("id", replaceElemIdStr);
54437                 obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
54438                 obj.style.display = "none";
54439                 (function(){
54440                     if (obj.readyState == 4) {
54441                         obj.parentNode.removeChild(obj);
54442                     }
54443                     else {
54444                         setTimeout(arguments.callee, 10);
54445                     }
54446                 })();
54447             }
54448             createSWF(att, par, replaceElemIdStr);
54449         }
54450     }
54451     
54452     /* Functions to abstract and display alternative content
54453     */
54454     function displayAltContent(obj) {
54455         if (ua.ie && ua.win && obj.readyState != 4) {
54456             // 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,
54457             // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
54458             var el = createElement("div");
54459             obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
54460             el.parentNode.replaceChild(abstractAltContent(obj), el);
54461             obj.style.display = "none";
54462             (function(){
54463                 if (obj.readyState == 4) {
54464                     obj.parentNode.removeChild(obj);
54465                 }
54466                 else {
54467                     setTimeout(arguments.callee, 10);
54468                 }
54469             })();
54470         }
54471         else {
54472             obj.parentNode.replaceChild(abstractAltContent(obj), obj);
54473         }
54474     } 
54475
54476     function abstractAltContent(obj) {
54477         var ac = createElement("div");
54478         if (ua.win && ua.ie) {
54479             ac.innerHTML = obj.innerHTML;
54480         }
54481         else {
54482             var nestedObj = obj.getElementsByTagName(OBJECT)[0];
54483             if (nestedObj) {
54484                 var c = nestedObj.childNodes;
54485                 if (c) {
54486                     var cl = c.length;
54487                     for (var i = 0; i < cl; i++) {
54488                         if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
54489                             ac.appendChild(c[i].cloneNode(true));
54490                         }
54491                     }
54492                 }
54493             }
54494         }
54495         return ac;
54496     }
54497     
54498     /* Cross-browser dynamic SWF creation
54499     */
54500     function createSWF(attObj, parObj, id) {
54501         var r, el = getElementById(id);
54502         if (ua.wk && ua.wk < 312) { return r; }
54503         if (el) {
54504             if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
54505                 attObj.id = id;
54506             }
54507             if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
54508                 var att = "";
54509                 for (var i in attObj) {
54510                     if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
54511                         if (i.toLowerCase() == "data") {
54512                             parObj.movie = attObj[i];
54513                         }
54514                         else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
54515                             att += ' class="' + attObj[i] + '"';
54516                         }
54517                         else if (i.toLowerCase() != "classid") {
54518                             att += ' ' + i + '="' + attObj[i] + '"';
54519                         }
54520                     }
54521                 }
54522                 var par = "";
54523                 for (var j in parObj) {
54524                     if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
54525                         par += '<param name="' + j + '" value="' + parObj[j] + '" />';
54526                     }
54527                 }
54528                 el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
54529                 objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
54530                 r = getElementById(attObj.id);  
54531             }
54532             else { // well-behaving browsers
54533                 var o = createElement(OBJECT);
54534                 o.setAttribute("type", FLASH_MIME_TYPE);
54535                 for (var m in attObj) {
54536                     if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
54537                         if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
54538                             o.setAttribute("class", attObj[m]);
54539                         }
54540                         else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
54541                             o.setAttribute(m, attObj[m]);
54542                         }
54543                     }
54544                 }
54545                 for (var n in parObj) {
54546                     if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
54547                         createObjParam(o, n, parObj[n]);
54548                     }
54549                 }
54550                 el.parentNode.replaceChild(o, el);
54551                 r = o;
54552             }
54553         }
54554         return r;
54555     }
54556     
54557     function createObjParam(el, pName, pValue) {
54558         var p = createElement("param");
54559         p.setAttribute("name", pName);  
54560         p.setAttribute("value", pValue);
54561         el.appendChild(p);
54562     }
54563     
54564     /* Cross-browser SWF removal
54565         - Especially needed to safely and completely remove a SWF in Internet Explorer
54566     */
54567     function removeSWF(id) {
54568         var obj = getElementById(id);
54569         if (obj && obj.nodeName == "OBJECT") {
54570             if (ua.ie && ua.win) {
54571                 obj.style.display = "none";
54572                 (function(){
54573                     if (obj.readyState == 4) {
54574                         removeObjectInIE(id);
54575                     }
54576                     else {
54577                         setTimeout(arguments.callee, 10);
54578                     }
54579                 })();
54580             }
54581             else {
54582                 obj.parentNode.removeChild(obj);
54583             }
54584         }
54585     }
54586     
54587     function removeObjectInIE(id) {
54588         var obj = getElementById(id);
54589         if (obj) {
54590             for (var i in obj) {
54591                 if (typeof obj[i] == "function") {
54592                     obj[i] = null;
54593                 }
54594             }
54595             obj.parentNode.removeChild(obj);
54596         }
54597     }
54598     
54599     /* Functions to optimize JavaScript compression
54600     */
54601     function getElementById(id) {
54602         var el = null;
54603         try {
54604             el = doc.getElementById(id);
54605         }
54606         catch (e) {}
54607         return el;
54608     }
54609     
54610     function createElement(el) {
54611         return doc.createElement(el);
54612     }
54613     
54614     /* Updated attachEvent function for Internet Explorer
54615         - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
54616     */  
54617     function addListener(target, eventType, fn) {
54618         target.attachEvent(eventType, fn);
54619         listenersArr[listenersArr.length] = [target, eventType, fn];
54620     }
54621     
54622     /* Flash Player and SWF content version matching
54623     */
54624     function hasPlayerVersion(rv) {
54625         var pv = ua.pv, v = rv.split(".");
54626         v[0] = parseInt(v[0], 10);
54627         v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
54628         v[2] = parseInt(v[2], 10) || 0;
54629         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;
54630     }
54631     
54632     /* Cross-browser dynamic CSS creation
54633         - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
54634     */  
54635     function createCSS(sel, decl, media, newStyle) {
54636         if (ua.ie && ua.mac) { return; }
54637         var h = doc.getElementsByTagName("head")[0];
54638         if (!h) { return; } // to also support badly authored HTML pages that lack a head element
54639         var m = (media && typeof media == "string") ? media : "screen";
54640         if (newStyle) {
54641             dynamicStylesheet = null;
54642             dynamicStylesheetMedia = null;
54643         }
54644         if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
54645             // create dynamic stylesheet + get a global reference to it
54646             var s = createElement("style");
54647             s.setAttribute("type", "text/css");
54648             s.setAttribute("media", m);
54649             dynamicStylesheet = h.appendChild(s);
54650             if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
54651                 dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
54652             }
54653             dynamicStylesheetMedia = m;
54654         }
54655         // add style rule
54656         if (ua.ie && ua.win) {
54657             if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
54658                 dynamicStylesheet.addRule(sel, decl);
54659             }
54660         }
54661         else {
54662             if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
54663                 dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
54664             }
54665         }
54666     }
54667     
54668     function setVisibility(id, isVisible) {
54669         if (!autoHideShow) { return; }
54670         var v = isVisible ? "visible" : "hidden";
54671         if (isDomLoaded && getElementById(id)) {
54672             getElementById(id).style.visibility = v;
54673         }
54674         else {
54675             createCSS("#" + id, "visibility:" + v);
54676         }
54677     }
54678
54679     /* Filter to avoid XSS attacks
54680     */
54681     function urlEncodeIfNecessary(s) {
54682         var regex = /[\\\"<>\.;]/;
54683         var hasBadChars = regex.exec(s) != null;
54684         return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
54685     }
54686     
54687     /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
54688     */
54689     var cleanup = function() {
54690         if (ua.ie && ua.win) {
54691             window.attachEvent("onunload", function() {
54692                 // remove listeners to avoid memory leaks
54693                 var ll = listenersArr.length;
54694                 for (var i = 0; i < ll; i++) {
54695                     listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
54696                 }
54697                 // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
54698                 var il = objIdArr.length;
54699                 for (var j = 0; j < il; j++) {
54700                     removeSWF(objIdArr[j]);
54701                 }
54702                 // cleanup library's main closures to avoid memory leaks
54703                 for (var k in ua) {
54704                     ua[k] = null;
54705                 }
54706                 ua = null;
54707                 for (var l in swfobject) {
54708                     swfobject[l] = null;
54709                 }
54710                 swfobject = null;
54711             });
54712         }
54713     }();
54714     
54715     return {
54716         /* Public API
54717             - Reference: http://code.google.com/p/swfobject/wiki/documentation
54718         */ 
54719         registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
54720             if (ua.w3 && objectIdStr && swfVersionStr) {
54721                 var regObj = {};
54722                 regObj.id = objectIdStr;
54723                 regObj.swfVersion = swfVersionStr;
54724                 regObj.expressInstall = xiSwfUrlStr;
54725                 regObj.callbackFn = callbackFn;
54726                 regObjArr[regObjArr.length] = regObj;
54727                 setVisibility(objectIdStr, false);
54728             }
54729             else if (callbackFn) {
54730                 callbackFn({success:false, id:objectIdStr});
54731             }
54732         },
54733         
54734         getObjectById: function(objectIdStr) {
54735             if (ua.w3) {
54736                 return getObjectById(objectIdStr);
54737             }
54738         },
54739         
54740         embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
54741             var callbackObj = {success:false, id:replaceElemIdStr};
54742             if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
54743                 setVisibility(replaceElemIdStr, false);
54744                 addDomLoadEvent(function() {
54745                     widthStr += ""; // auto-convert to string
54746                     heightStr += "";
54747                     var att = {};
54748                     if (attObj && typeof attObj === OBJECT) {
54749                         for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
54750                             att[i] = attObj[i];
54751                         }
54752                     }
54753                     att.data = swfUrlStr;
54754                     att.width = widthStr;
54755                     att.height = heightStr;
54756                     var par = {}; 
54757                     if (parObj && typeof parObj === OBJECT) {
54758                         for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
54759                             par[j] = parObj[j];
54760                         }
54761                     }
54762                     if (flashvarsObj && typeof flashvarsObj === OBJECT) {
54763                         for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
54764                             if (typeof par.flashvars != UNDEF) {
54765                                 par.flashvars += "&" + k + "=" + flashvarsObj[k];
54766                             }
54767                             else {
54768                                 par.flashvars = k + "=" + flashvarsObj[k];
54769                             }
54770                         }
54771                     }
54772                     if (hasPlayerVersion(swfVersionStr)) { // create SWF
54773                         var obj = createSWF(att, par, replaceElemIdStr);
54774                         if (att.id == replaceElemIdStr) {
54775                             setVisibility(replaceElemIdStr, true);
54776                         }
54777                         callbackObj.success = true;
54778                         callbackObj.ref = obj;
54779                     }
54780                     else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
54781                         att.data = xiSwfUrlStr;
54782                         showExpressInstall(att, par, replaceElemIdStr, callbackFn);
54783                         return;
54784                     }
54785                     else { // show alternative content
54786                         setVisibility(replaceElemIdStr, true);
54787                     }
54788                     if (callbackFn) { callbackFn(callbackObj); }
54789                 });
54790             }
54791             else if (callbackFn) { callbackFn(callbackObj); }
54792         },
54793         
54794         switchOffAutoHideShow: function() {
54795             autoHideShow = false;
54796         },
54797         
54798         ua: ua,
54799         
54800         getFlashPlayerVersion: function() {
54801             return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
54802         },
54803         
54804         hasFlashPlayerVersion: hasPlayerVersion,
54805         
54806         createSWF: function(attObj, parObj, replaceElemIdStr) {
54807             if (ua.w3) {
54808                 return createSWF(attObj, parObj, replaceElemIdStr);
54809             }
54810             else {
54811                 return undefined;
54812             }
54813         },
54814         
54815         showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
54816             if (ua.w3 && canExpressInstall()) {
54817                 showExpressInstall(att, par, replaceElemIdStr, callbackFn);
54818             }
54819         },
54820         
54821         removeSWF: function(objElemIdStr) {
54822             if (ua.w3) {
54823                 removeSWF(objElemIdStr);
54824             }
54825         },
54826         
54827         createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
54828             if (ua.w3) {
54829                 createCSS(selStr, declStr, mediaStr, newStyleBoolean);
54830             }
54831         },
54832         
54833         addDomLoadEvent: addDomLoadEvent,
54834         
54835         addLoadEvent: addLoadEvent,
54836         
54837         getQueryParamValue: function(param) {
54838             var q = doc.location.search || doc.location.hash;
54839             if (q) {
54840                 if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
54841                 if (param == null) {
54842                     return urlEncodeIfNecessary(q);
54843                 }
54844                 var pairs = q.split("&");
54845                 for (var i = 0; i < pairs.length; i++) {
54846                     if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
54847                         return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
54848                     }
54849                 }
54850             }
54851             return "";
54852         },
54853         
54854         // For internal usage only
54855         expressInstallCallback: function() {
54856             if (isExpressInstallActive) {
54857                 var obj = getElementById(EXPRESS_INSTALL_ID);
54858                 if (obj && storedAltContent) {
54859                     obj.parentNode.replaceChild(storedAltContent, obj);
54860                     if (storedAltContentId) {
54861                         setVisibility(storedAltContentId, true);
54862                         if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
54863                     }
54864                     if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
54865                 }
54866                 isExpressInstallActive = false;
54867             } 
54868         }
54869     };
54870 }();
54871 /**
54872  * @class Ext.FlashComponent
54873  * @extends Ext.BoxComponent
54874  * @constructor
54875  * @xtype flash
54876  */
54877 Ext.FlashComponent = Ext.extend(Ext.BoxComponent, {
54878     /**
54879      * @cfg {String} flashVersion
54880      * Indicates the version the flash content was published for. Defaults to <tt>'9.0.115'</tt>.
54881      */
54882     flashVersion : '9.0.115',
54883
54884     /**
54885      * @cfg {String} backgroundColor
54886      * The background color of the chart. Defaults to <tt>'#ffffff'</tt>.
54887      */
54888     backgroundColor: '#ffffff',
54889
54890     /**
54891      * @cfg {String} wmode
54892      * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
54893      */
54894     wmode: 'opaque',
54895
54896     /**
54897      * @cfg {Object} flashVars
54898      * A set of key value pairs to be passed to the flash object as flash variables. Defaults to <tt>undefined</tt>.
54899      */
54900     flashVars: undefined,
54901
54902     /**
54903      * @cfg {Object} flashParams
54904      * A set of key value pairs to be passed to the flash object as parameters. Possible parameters can be found here:
54905      * http://kb2.adobe.com/cps/127/tn_12701.html Defaults to <tt>undefined</tt>.
54906      */
54907     flashParams: undefined,
54908
54909     /**
54910      * @cfg {String} url
54911      * The URL of the chart to include. Defaults to <tt>undefined</tt>.
54912      */
54913     url: undefined,
54914     swfId : undefined,
54915     swfWidth: '100%',
54916     swfHeight: '100%',
54917
54918     /**
54919      * @cfg {Boolean} expressInstall
54920      * True to prompt the user to install flash if not installed. Note that this uses
54921      * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
54922      */
54923     expressInstall: false,
54924
54925     initComponent : function(){
54926         Ext.FlashComponent.superclass.initComponent.call(this);
54927
54928         this.addEvents(
54929             /**
54930              * @event initialize
54931              *
54932              * @param {Chart} this
54933              */
54934             'initialize'
54935         );
54936     },
54937
54938     onRender : function(){
54939         Ext.FlashComponent.superclass.onRender.apply(this, arguments);
54940
54941         var params = Ext.apply({
54942             allowScriptAccess: 'always',
54943             bgcolor: this.backgroundColor,
54944             wmode: this.wmode
54945         }, this.flashParams), vars = Ext.apply({
54946             allowedDomain: document.location.hostname,
54947             YUISwfId: this.getId(),
54948             YUIBridgeCallback: 'Ext.FlashEventProxy.onEvent'
54949         }, this.flashVars);
54950
54951         new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
54952             this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);
54953
54954         this.swf = Ext.getDom(this.id);
54955         this.el = Ext.get(this.swf);
54956     },
54957
54958     getSwfId : function(){
54959         return this.swfId || (this.swfId = "extswf" + (++Ext.Component.AUTO_ID));
54960     },
54961
54962     getId : function(){
54963         return this.id || (this.id = "extflashcmp" + (++Ext.Component.AUTO_ID));
54964     },
54965
54966     onFlashEvent : function(e){
54967         switch(e.type){
54968             case "swfReady":
54969                 this.initSwf();
54970                 return;
54971             case "log":
54972                 return;
54973         }
54974         e.component = this;
54975         this.fireEvent(e.type.toLowerCase().replace(/event$/, ''), e);
54976     },
54977
54978     initSwf : function(){
54979         this.onSwfReady(!!this.isInitialized);
54980         this.isInitialized = true;
54981         this.fireEvent('initialize', this);
54982     },
54983
54984     beforeDestroy: function(){
54985         if(this.rendered){
54986             swfobject.removeSWF(this.swf.id);
54987         }
54988         Ext.FlashComponent.superclass.beforeDestroy.call(this);
54989     },
54990
54991     onSwfReady : Ext.emptyFn
54992 });
54993
54994 /**
54995  * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
54996  * @static
54997  * @type String
54998  */
54999 Ext.FlashComponent.EXPRESS_INSTALL_URL = 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf';
55000
55001 Ext.reg('flash', Ext.FlashComponent);/**
55002  * @class Ext.FlashProxy
55003  * @singleton
55004  */
55005 Ext.FlashEventProxy = {
55006     onEvent : function(id, e){
55007         var fp = Ext.getCmp(id);
55008         if(fp){
55009             fp.onFlashEvent(e);
55010         }else{
55011             arguments.callee.defer(10, this, [id, e]);
55012         }
55013     }
55014 };/**
55015  * @class Ext.chart.Chart
55016  * @extends Ext.FlashComponent
55017  * The Ext.chart package provides the capability to visualize data with flash based charting.
55018  * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.
55019  * To change the look and feel of a chart, see the {@link #chartStyle} and {@link #extraStyle} config options.
55020  * @constructor
55021  * @xtype chart
55022  */
55023
55024  Ext.chart.Chart = Ext.extend(Ext.FlashComponent, {
55025     refreshBuffer: 100,
55026
55027     /**
55028      * @cfg {String} backgroundColor
55029      * @hide
55030      */
55031
55032     /**
55033      * @cfg {Object} chartStyle
55034      * Sets styles for this chart. This contains default styling, so modifying this property will <b>override</b>
55035      * the built in styles of the chart. Use {@link #extraStyle} to add customizations to the default styling.
55036      */
55037     chartStyle: {
55038         padding: 10,
55039         animationEnabled: true,
55040         font: {
55041             name: 'Tahoma',
55042             color: 0x444444,
55043             size: 11
55044         },
55045         dataTip: {
55046             padding: 5,
55047             border: {
55048                 color: 0x99bbe8,
55049                 size:1
55050             },
55051             background: {
55052                 color: 0xDAE7F6,
55053                 alpha: .9
55054             },
55055             font: {
55056                 name: 'Tahoma',
55057                 color: 0x15428B,
55058                 size: 10,
55059                 bold: true
55060             }
55061         }
55062     },
55063
55064     /**
55065      * @cfg {String} url
55066      * The url to load the chart from. This defaults to Ext.chart.Chart.CHART_URL, which should
55067      * be modified to point to the local charts resource.
55068      */
55069
55070     /**
55071      * @cfg {Object} extraStyle
55072      * Contains extra styles that will be added or overwritten to the default chartStyle. Defaults to <tt>null</tt>.
55073      * For a detailed list of the options available, visit the YUI Charts site
55074      * at <a href="http://developer.yahoo.com/yui/charts/#basicstyles">http://developer.yahoo.com/yui/charts/#basicstyles</a><br/>
55075      * Some of the options availabe:<br />
55076      * <ul style="padding:5px;padding-left:16px;list-style-type:inherit;">
55077      * <li><b>padding</b> - The space around the edge of the chart's contents. Padding does not increase the size of the chart.</li>
55078      * <li><b>animationEnabled</b> - A Boolean value that specifies whether marker animations are enabled or not. Enabled by default.</li>
55079      * <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/>
55080      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55081      *      <li><b>name</b> - font name</li>
55082      *      <li><b>color</b> - font color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
55083      *      <li><b>size</b> - font size in points (numeric portion only, ie: 11)</li>
55084      *      <li><b>bold</b> - boolean</li>
55085      *      <li><b>italic</b> - boolean</li>
55086      *      <li><b>underline</b> - boolean</li>
55087      *  </ul>
55088      * </li>
55089      * <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/>
55090      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55091      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
55092      *      <li><b>size</b> - border size in pixels (numeric portion only, ie: 1)</li>
55093      *  </ul>
55094      * </li>
55095      * <li><b>background</b> - An object defining the background style of the chart.<br/>
55096      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55097      *      <li><b>color</b> - border color (hex code, ie: "#ff0000", "ff0000" or 0xff0000)</li>
55098      *      <li><b>image</b> - an image URL. May be relative to the current document or absolute.</li>
55099      *  </ul>
55100      * </li>
55101      * <li><b>legend</b> - An object defining the legend style<br/>
55102      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55103      *      <li><b>display</b> - location of the legend. Possible values are "none", "left", "right", "top", and "bottom".</li>
55104      *      <li><b>spacing</b> - an image URL. May be relative to the current document or absolute.</li>
55105      *      <li><b>padding, border, background, font</b> - same options as described above.</li>
55106      *  </ul></li>
55107      * <li><b>dataTip</b> - An object defining the style of the data tip (tooltip).<br/>
55108      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55109      *      <li><b>padding, border, background, font</b> - same options as described above.</li>
55110      *  </ul></li>
55111      * <li><b>xAxis and yAxis</b> - An object defining the style of the style of either axis.<br/>
55112      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55113      *      <li><b>color</b> - same option as described above.</li>
55114      *      <li><b>size</b> - same option as described above.</li>
55115      *      <li><b>showLabels</b> - boolean</li>
55116      *      <li><b>labelRotation</b> - a value in degrees from -90 through 90. Default is zero.</li>
55117      *  </ul></li>
55118      * <li><b>majorGridLines and minorGridLines</b> - An object defining the style of the style of the grid lines.<br/>
55119      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55120      *      <li><b>color, size</b> - same options as described above.</li>
55121      *  </ul></li></li>
55122      * <li><b>zeroGridLine</b> - An object defining the style of the style of the zero grid line.<br/>
55123      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55124      *      <li><b>color, size</b> - same options as described above.</li>
55125      *  </ul></li></li>
55126      * <li><b>majorTicks and minorTicks</b> - An object defining the style of the style of ticks in the chart.<br/>
55127      *  <ul style="padding:5px;padding-left:26px;list-style-type:circle;">
55128      *      <li><b>color, size</b> - same options as described above.</li>
55129      *      <li><b>length</b> - the length of each tick in pixels extending from the axis.</li>
55130      *      <li><b>display</b> - how the ticks are drawn. Possible values are "none", "inside", "outside", and "cross".</li>
55131      *  </ul></li></li>
55132      * </ul>
55133      */
55134     extraStyle: null,
55135
55136     /**
55137      * @cfg {Object} seriesStyles
55138      * Contains styles to apply to the series after a refresh. Defaults to <tt>null</tt>.
55139      */
55140     seriesStyles: null,
55141
55142     /**
55143      * @cfg {Boolean} disableCaching
55144      * True to add a "cache buster" to the end of the chart url. Defaults to true for Opera and IE.
55145      */
55146     disableCaching: Ext.isIE || Ext.isOpera,
55147     disableCacheParam: '_dc',
55148
55149     initComponent : function(){
55150         Ext.chart.Chart.superclass.initComponent.call(this);
55151         if(!this.url){
55152             this.url = Ext.chart.Chart.CHART_URL;
55153         }
55154         if(this.disableCaching){
55155             this.url = Ext.urlAppend(this.url, String.format('{0}={1}', this.disableCacheParam, new Date().getTime()));
55156         }
55157         this.addEvents(
55158             'itemmouseover',
55159             'itemmouseout',
55160             'itemclick',
55161             'itemdoubleclick',
55162             'itemdragstart',
55163             'itemdrag',
55164             'itemdragend',
55165             /**
55166              * @event beforerefresh
55167              * Fires before a refresh to the chart data is called.  If the beforerefresh handler returns
55168              * <tt>false</tt> the {@link #refresh} action will be cancelled.
55169              * @param {Chart} this
55170              */
55171             'beforerefresh',
55172             /**
55173              * @event refresh
55174              * Fires after the chart data has been refreshed.
55175              * @param {Chart} this
55176              */
55177             'refresh'
55178         );
55179         this.store = Ext.StoreMgr.lookup(this.store);
55180     },
55181
55182     /**
55183      * Sets a single style value on the Chart instance.
55184      *
55185      * @param name {String} Name of the Chart style value to change.
55186      * @param value {Object} New value to pass to the Chart style.
55187      */
55188      setStyle: function(name, value){
55189          this.swf.setStyle(name, Ext.encode(value));
55190      },
55191
55192     /**
55193      * Resets all styles on the Chart instance.
55194      *
55195      * @param styles {Object} Initializer for all Chart styles.
55196      */
55197     setStyles: function(styles){
55198         this.swf.setStyles(Ext.encode(styles));
55199     },
55200
55201     /**
55202      * Sets the styles on all series in the Chart.
55203      *
55204      * @param styles {Array} Initializer for all Chart series styles.
55205      */
55206     setSeriesStyles: function(styles){
55207         this.seriesStyles = styles;
55208         var s = [];
55209         Ext.each(styles, function(style){
55210             s.push(Ext.encode(style));
55211         });
55212         this.swf.setSeriesStyles(s);
55213     },
55214
55215     setCategoryNames : function(names){
55216         this.swf.setCategoryNames(names);
55217     },
55218
55219     setLegendRenderer : function(fn, scope){
55220         var chart = this;
55221         scope = scope || chart;
55222         chart.removeFnProxy(chart.legendFnName);
55223         chart.legendFnName = chart.createFnProxy(function(name){
55224             return fn.call(scope, name);
55225         });
55226         chart.swf.setLegendLabelFunction(chart.legendFnName);
55227     },
55228
55229     setTipRenderer : function(fn, scope){
55230         var chart = this;
55231         scope = scope || chart;
55232         chart.removeFnProxy(chart.tipFnName);
55233         chart.tipFnName = chart.createFnProxy(function(item, index, series){
55234             var record = chart.store.getAt(index);
55235             return fn.call(scope, chart, record, index, series);
55236         });
55237         chart.swf.setDataTipFunction(chart.tipFnName);
55238     },
55239
55240     setSeries : function(series){
55241         this.series = series;
55242         this.refresh();
55243     },
55244
55245     /**
55246      * Changes the data store bound to this chart and refreshes it.
55247      * @param {Store} store The store to bind to this chart
55248      */
55249     bindStore : function(store, initial){
55250         if(!initial && this.store){
55251             if(store !== this.store && this.store.autoDestroy){
55252                 this.store.destroy();
55253             }else{
55254                 this.store.un("datachanged", this.refresh, this);
55255                 this.store.un("add", this.delayRefresh, this);
55256                 this.store.un("remove", this.delayRefresh, this);
55257                 this.store.un("update", this.delayRefresh, this);
55258                 this.store.un("clear", this.refresh, this);
55259             }
55260         }
55261         if(store){
55262             store = Ext.StoreMgr.lookup(store);
55263             store.on({
55264                 scope: this,
55265                 datachanged: this.refresh,
55266                 add: this.delayRefresh,
55267                 remove: this.delayRefresh,
55268                 update: this.delayRefresh,
55269                 clear: this.refresh
55270             });
55271         }
55272         this.store = store;
55273         if(store && !initial){
55274             this.refresh();
55275         }
55276     },
55277
55278     onSwfReady : function(isReset){
55279         Ext.chart.Chart.superclass.onSwfReady.call(this, isReset);
55280         var ref;
55281         this.swf.setType(this.type);
55282
55283         if(this.chartStyle){
55284             this.setStyles(Ext.apply({}, this.extraStyle, this.chartStyle));
55285         }
55286
55287         if(this.categoryNames){
55288             this.setCategoryNames(this.categoryNames);
55289         }
55290
55291         if(this.tipRenderer){
55292             ref = this.getFunctionRef(this.tipRenderer);
55293             this.setTipRenderer(ref.fn, ref.scope);
55294         }
55295         if(this.legendRenderer){
55296             ref = this.getFunctionRef(this.legendRenderer);
55297             this.setLegendRenderer(ref.fn, ref.scope);
55298         }
55299         if(!isReset){
55300             this.bindStore(this.store, true);
55301         }
55302         this.refresh.defer(10, this);
55303     },
55304
55305     delayRefresh : function(){
55306         if(!this.refreshTask){
55307             this.refreshTask = new Ext.util.DelayedTask(this.refresh, this);
55308         }
55309         this.refreshTask.delay(this.refreshBuffer);
55310     },
55311
55312     refresh : function(){
55313         if(this.fireEvent('beforerefresh', this) !== false){
55314             var styleChanged = false;
55315             // convert the store data into something YUI charts can understand
55316             var data = [], rs = this.store.data.items;
55317             for(var j = 0, len = rs.length; j < len; j++){
55318                 data[j] = rs[j].data;
55319             }
55320             //make a copy of the series definitions so that we aren't
55321             //editing them directly.
55322             var dataProvider = [];
55323             var seriesCount = 0;
55324             var currentSeries = null;
55325             var i = 0;
55326             if(this.series){
55327                 seriesCount = this.series.length;
55328                 for(i = 0; i < seriesCount; i++){
55329                     currentSeries = this.series[i];
55330                     var clonedSeries = {};
55331                     for(var prop in currentSeries){
55332                         if(prop == "style" && currentSeries.style !== null){
55333                             clonedSeries.style = Ext.encode(currentSeries.style);
55334                             styleChanged = true;
55335                             //we don't want to modify the styles again next time
55336                             //so null out the style property.
55337                             // this causes issues
55338                             // currentSeries.style = null;
55339                         } else{
55340                             clonedSeries[prop] = currentSeries[prop];
55341                         }
55342                     }
55343                     dataProvider.push(clonedSeries);
55344                 }
55345             }
55346
55347             if(seriesCount > 0){
55348                 for(i = 0; i < seriesCount; i++){
55349                     currentSeries = dataProvider[i];
55350                     if(!currentSeries.type){
55351                         currentSeries.type = this.type;
55352                     }
55353                     currentSeries.dataProvider = data;
55354                 }
55355             } else{
55356                 dataProvider.push({type: this.type, dataProvider: data});
55357             }
55358             this.swf.setDataProvider(dataProvider);
55359             if(this.seriesStyles){
55360                 this.setSeriesStyles(this.seriesStyles);
55361             }
55362             this.fireEvent('refresh', this);
55363         }
55364     },
55365
55366     // private
55367     createFnProxy : function(fn){
55368         var fnName = 'extFnProxy' + (++Ext.chart.Chart.PROXY_FN_ID);
55369         Ext.chart.Chart.proxyFunction[fnName] = fn;
55370         return 'Ext.chart.Chart.proxyFunction.' + fnName;
55371     },
55372
55373     // private
55374     removeFnProxy : function(fn){
55375         if(!Ext.isEmpty(fn)){
55376             fn = fn.replace('Ext.chart.Chart.proxyFunction.', '');
55377             delete Ext.chart.Chart.proxyFunction[fn];
55378         }
55379     },
55380
55381     // private
55382     getFunctionRef : function(val){
55383         if(Ext.isFunction(val)){
55384             return {
55385                 fn: val,
55386                 scope: this
55387             };
55388         }else{
55389             return {
55390                 fn: val.fn,
55391                 scope: val.scope || this
55392             }
55393         }
55394     },
55395
55396     // private
55397     onDestroy: function(){
55398         if (this.refreshTask && this.refreshTask.cancel){
55399             this.refreshTask.cancel();
55400         }
55401         Ext.chart.Chart.superclass.onDestroy.call(this);
55402         this.bindStore(null);
55403         this.removeFnProxy(this.tipFnName);
55404         this.removeFnProxy(this.legendFnName);
55405     }
55406 });
55407 Ext.reg('chart', Ext.chart.Chart);
55408 Ext.chart.Chart.PROXY_FN_ID = 0;
55409 Ext.chart.Chart.proxyFunction = {};
55410
55411 /**
55412  * Sets the url to load the chart from. This should be set to a local resource.
55413  * @static
55414  * @type String
55415  */
55416 Ext.chart.Chart.CHART_URL = 'http:/' + '/yui.yahooapis.com/2.8.0/build/charts/assets/charts.swf';
55417
55418 /**
55419  * @class Ext.chart.PieChart
55420  * @extends Ext.chart.Chart
55421  * @constructor
55422  * @xtype piechart
55423  */
55424 Ext.chart.PieChart = Ext.extend(Ext.chart.Chart, {
55425     type: 'pie',
55426
55427     onSwfReady : function(isReset){
55428         Ext.chart.PieChart.superclass.onSwfReady.call(this, isReset);
55429
55430         this.setDataField(this.dataField);
55431         this.setCategoryField(this.categoryField);
55432     },
55433
55434     setDataField : function(field){
55435         this.dataField = field;
55436         this.swf.setDataField(field);
55437     },
55438
55439     setCategoryField : function(field){
55440         this.categoryField = field;
55441         this.swf.setCategoryField(field);
55442     }
55443 });
55444 Ext.reg('piechart', Ext.chart.PieChart);
55445
55446 /**
55447  * @class Ext.chart.CartesianChart
55448  * @extends Ext.chart.Chart
55449  * @constructor
55450  * @xtype cartesianchart
55451  */
55452 Ext.chart.CartesianChart = Ext.extend(Ext.chart.Chart, {
55453     onSwfReady : function(isReset){
55454         Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);
55455         this.labelFn = [];
55456         if(this.xField){
55457             this.setXField(this.xField);
55458         }
55459         if(this.yField){
55460             this.setYField(this.yField);
55461         }
55462         if(this.xAxis){
55463             this.setXAxis(this.xAxis);
55464         }
55465         if(this.xAxes){
55466             this.setXAxes(this.xAxes);
55467         }
55468         if(this.yAxis){
55469             this.setYAxis(this.yAxis);
55470         }
55471         if(this.yAxes){
55472             this.setYAxes(this.yAxes);
55473         }
55474         if(Ext.isDefined(this.constrainViewport)){
55475             this.swf.setConstrainViewport(this.constrainViewport);
55476         }
55477     },
55478
55479     setXField : function(value){
55480         this.xField = value;
55481         this.swf.setHorizontalField(value);
55482     },
55483
55484     setYField : function(value){
55485         this.yField = value;
55486         this.swf.setVerticalField(value);
55487     },
55488
55489     setXAxis : function(value){
55490         this.xAxis = this.createAxis('xAxis', value);
55491         this.swf.setHorizontalAxis(this.xAxis);
55492     },
55493
55494     setXAxes : function(value){
55495         var axis;
55496         for(var i = 0; i < value.length; i++) {
55497             axis = this.createAxis('xAxis' + i, value[i]);
55498             this.swf.setHorizontalAxis(axis);
55499         }
55500     },
55501
55502     setYAxis : function(value){
55503         this.yAxis = this.createAxis('yAxis', value);
55504         this.swf.setVerticalAxis(this.yAxis);
55505     },
55506
55507     setYAxes : function(value){
55508         var axis;
55509         for(var i = 0; i < value.length; i++) {
55510             axis = this.createAxis('yAxis' + i, value[i]);
55511             this.swf.setVerticalAxis(axis);
55512         }
55513     },
55514
55515     createAxis : function(axis, value){
55516         var o = Ext.apply({}, value),
55517             ref,
55518             old;
55519
55520         if(this[axis]){
55521             old = this[axis].labelFunction;
55522             this.removeFnProxy(old);
55523             this.labelFn.remove(old);
55524         }
55525         if(o.labelRenderer){
55526             ref = this.getFunctionRef(o.labelRenderer);
55527             o.labelFunction = this.createFnProxy(function(v){
55528                 return ref.fn.call(ref.scope, v);
55529             });
55530             delete o.labelRenderer;
55531             this.labelFn.push(o.labelFunction);
55532         }
55533         if(axis.indexOf('xAxis') > -1 && o.position == 'left'){
55534             o.position = 'bottom';
55535         }
55536         return o;
55537     },
55538
55539     onDestroy : function(){
55540         Ext.chart.CartesianChart.superclass.onDestroy.call(this);
55541         Ext.each(this.labelFn, function(fn){
55542             this.removeFnProxy(fn);
55543         }, this);
55544     }
55545 });
55546 Ext.reg('cartesianchart', Ext.chart.CartesianChart);
55547
55548 /**
55549  * @class Ext.chart.LineChart
55550  * @extends Ext.chart.CartesianChart
55551  * @constructor
55552  * @xtype linechart
55553  */
55554 Ext.chart.LineChart = Ext.extend(Ext.chart.CartesianChart, {
55555     type: 'line'
55556 });
55557 Ext.reg('linechart', Ext.chart.LineChart);
55558
55559 /**
55560  * @class Ext.chart.ColumnChart
55561  * @extends Ext.chart.CartesianChart
55562  * @constructor
55563  * @xtype columnchart
55564  */
55565 Ext.chart.ColumnChart = Ext.extend(Ext.chart.CartesianChart, {
55566     type: 'column'
55567 });
55568 Ext.reg('columnchart', Ext.chart.ColumnChart);
55569
55570 /**
55571  * @class Ext.chart.StackedColumnChart
55572  * @extends Ext.chart.CartesianChart
55573  * @constructor
55574  * @xtype stackedcolumnchart
55575  */
55576 Ext.chart.StackedColumnChart = Ext.extend(Ext.chart.CartesianChart, {
55577     type: 'stackcolumn'
55578 });
55579 Ext.reg('stackedcolumnchart', Ext.chart.StackedColumnChart);
55580
55581 /**
55582  * @class Ext.chart.BarChart
55583  * @extends Ext.chart.CartesianChart
55584  * @constructor
55585  * @xtype barchart
55586  */
55587 Ext.chart.BarChart = Ext.extend(Ext.chart.CartesianChart, {
55588     type: 'bar'
55589 });
55590 Ext.reg('barchart', Ext.chart.BarChart);
55591
55592 /**
55593  * @class Ext.chart.StackedBarChart
55594  * @extends Ext.chart.CartesianChart
55595  * @constructor
55596  * @xtype stackedbarchart
55597  */
55598 Ext.chart.StackedBarChart = Ext.extend(Ext.chart.CartesianChart, {
55599     type: 'stackbar'
55600 });
55601 Ext.reg('stackedbarchart', Ext.chart.StackedBarChart);
55602
55603
55604
55605 /**
55606  * @class Ext.chart.Axis
55607  * Defines a CartesianChart's vertical or horizontal axis.
55608  * @constructor
55609  */
55610 Ext.chart.Axis = function(config){
55611     Ext.apply(this, config);
55612 };
55613
55614 Ext.chart.Axis.prototype =
55615 {
55616     /**
55617      * The type of axis.
55618      *
55619      * @property type
55620      * @type String
55621      */
55622     type: null,
55623
55624     /**
55625      * The direction in which the axis is drawn. May be "horizontal" or "vertical".
55626      *
55627      * @property orientation
55628      * @type String
55629      */
55630     orientation: "horizontal",
55631
55632     /**
55633      * If true, the items on the axis will be drawn in opposite direction.
55634      *
55635      * @property reverse
55636      * @type Boolean
55637      */
55638     reverse: false,
55639
55640     /**
55641      * A string reference to the globally-accessible function that may be called to
55642      * determine each of the label values for this axis.
55643      *
55644      * @property labelFunction
55645      * @type String
55646      */
55647     labelFunction: null,
55648
55649     /**
55650      * If true, labels that overlap previously drawn labels on the axis will be hidden.
55651      *
55652      * @property hideOverlappingLabels
55653      * @type Boolean
55654      */
55655     hideOverlappingLabels: true,
55656
55657     /**
55658      * The space, in pixels, between labels on an axis.
55659      *
55660      * @property labelSpacing
55661      * @type Number
55662      */
55663     labelSpacing: 2
55664 };
55665
55666 /**
55667  * @class Ext.chart.NumericAxis
55668  * @extends Ext.chart.Axis
55669  * A type of axis whose units are measured in numeric values.
55670  * @constructor
55671  */
55672 Ext.chart.NumericAxis = Ext.extend(Ext.chart.Axis, {
55673     type: "numeric",
55674
55675     /**
55676      * The minimum value drawn by the axis. If not set explicitly, the axis
55677      * minimum will be calculated automatically.
55678      *
55679      * @property minimum
55680      * @type Number
55681      */
55682     minimum: NaN,
55683
55684     /**
55685      * The maximum value drawn by the axis. If not set explicitly, the axis
55686      * maximum will be calculated automatically.
55687      *
55688      * @property maximum
55689      * @type Number
55690      */
55691     maximum: NaN,
55692
55693     /**
55694      * The spacing between major intervals on this axis.
55695      *
55696      * @property majorUnit
55697      * @type Number
55698      */
55699     majorUnit: NaN,
55700
55701     /**
55702      * The spacing between minor intervals on this axis.
55703      *
55704      * @property minorUnit
55705      * @type Number
55706      */
55707     minorUnit: NaN,
55708
55709     /**
55710      * If true, the labels, ticks, gridlines, and other objects will snap to the
55711      * nearest major or minor unit. If false, their position will be based on
55712      * the minimum value.
55713      *
55714      * @property snapToUnits
55715      * @type Boolean
55716      */
55717     snapToUnits: true,
55718
55719     /**
55720      * If true, and the bounds are calculated automatically, either the minimum
55721      * or maximum will be set to zero.
55722      *
55723      * @property alwaysShowZero
55724      * @type Boolean
55725      */
55726     alwaysShowZero: true,
55727
55728     /**
55729      * The scaling algorithm to use on this axis. May be "linear" or
55730      * "logarithmic".
55731      *
55732      * @property scale
55733      * @type String
55734      */
55735     scale: "linear",
55736
55737     /**
55738      * Indicates whether to round the major unit.
55739      *
55740      * @property roundMajorUnit
55741      * @type Boolean
55742      */
55743     roundMajorUnit: true,
55744
55745     /**
55746      * Indicates whether to factor in the size of the labels when calculating a
55747      * major unit.
55748      *
55749      * @property calculateByLabelSize
55750      * @type Boolean
55751      */
55752     calculateByLabelSize: true,
55753
55754     /**
55755      * Indicates the position of the axis relative to the chart
55756      *
55757      * @property position
55758      * @type String
55759      */
55760     position: 'left',
55761
55762     /**
55763      * Indicates whether to extend maximum beyond data's maximum to the nearest
55764      * majorUnit.
55765      *
55766      * @property adjustMaximumByMajorUnit
55767      * @type Boolean
55768      */
55769     adjustMaximumByMajorUnit: true,
55770
55771     /**
55772      * Indicates whether to extend the minimum beyond data's minimum to the
55773      * nearest majorUnit.
55774      *
55775      * @property adjustMinimumByMajorUnit
55776      * @type Boolean
55777      */
55778     adjustMinimumByMajorUnit: true
55779
55780 });
55781
55782 /**
55783  * @class Ext.chart.TimeAxis
55784  * @extends Ext.chart.Axis
55785  * A type of axis whose units are measured in time-based values.
55786  * @constructor
55787  */
55788 Ext.chart.TimeAxis = Ext.extend(Ext.chart.Axis, {
55789     type: "time",
55790
55791     /**
55792      * The minimum value drawn by the axis. If not set explicitly, the axis
55793      * minimum will be calculated automatically.
55794      *
55795      * @property minimum
55796      * @type Date
55797      */
55798     minimum: null,
55799
55800     /**
55801      * The maximum value drawn by the axis. If not set explicitly, the axis
55802      * maximum will be calculated automatically.
55803      *
55804      * @property maximum
55805      * @type Number
55806      */
55807     maximum: null,
55808
55809     /**
55810      * The spacing between major intervals on this axis.
55811      *
55812      * @property majorUnit
55813      * @type Number
55814      */
55815     majorUnit: NaN,
55816
55817     /**
55818      * The time unit used by the majorUnit.
55819      *
55820      * @property majorTimeUnit
55821      * @type String
55822      */
55823     majorTimeUnit: null,
55824
55825     /**
55826      * The spacing between minor intervals on this axis.
55827      *
55828      * @property majorUnit
55829      * @type Number
55830      */
55831     minorUnit: NaN,
55832
55833     /**
55834      * The time unit used by the minorUnit.
55835      *
55836      * @property majorTimeUnit
55837      * @type String
55838      */
55839     minorTimeUnit: null,
55840
55841     /**
55842      * If true, the labels, ticks, gridlines, and other objects will snap to the
55843      * nearest major or minor unit. If false, their position will be based on
55844      * the minimum value.
55845      *
55846      * @property snapToUnits
55847      * @type Boolean
55848      */
55849     snapToUnits: true,
55850
55851     /**
55852      * Series that are stackable will only stack when this value is set to true.
55853      *
55854      * @property stackingEnabled
55855      * @type Boolean
55856      */
55857     stackingEnabled: false,
55858
55859     /**
55860      * Indicates whether to factor in the size of the labels when calculating a
55861      * major unit.
55862      *
55863      * @property calculateByLabelSize
55864      * @type Boolean
55865      */
55866     calculateByLabelSize: true
55867
55868 });
55869
55870 /**
55871  * @class Ext.chart.CategoryAxis
55872  * @extends Ext.chart.Axis
55873  * A type of axis that displays items in categories.
55874  * @constructor
55875  */
55876 Ext.chart.CategoryAxis = Ext.extend(Ext.chart.Axis, {
55877     type: "category",
55878
55879     /**
55880      * A list of category names to display along this axis.
55881      *
55882      * @property categoryNames
55883      * @type Array
55884      */
55885     categoryNames: null,
55886
55887     /**
55888      * Indicates whether or not to calculate the number of categories (ticks and
55889      * labels) when there is not enough room to display all labels on the axis.
55890      * If set to true, the axis will determine the number of categories to plot.
55891      * If not, all categories will be plotted.
55892      *
55893      * @property calculateCategoryCount
55894      * @type Boolean
55895      */
55896     calculateCategoryCount: false
55897
55898 });
55899
55900 /**
55901  * @class Ext.chart.Series
55902  * Series class for the charts widget.
55903  * @constructor
55904  */
55905 Ext.chart.Series = function(config) { Ext.apply(this, config); };
55906
55907 Ext.chart.Series.prototype =
55908 {
55909     /**
55910      * The type of series.
55911      *
55912      * @property type
55913      * @type String
55914      */
55915     type: null,
55916
55917     /**
55918      * The human-readable name of the series.
55919      *
55920      * @property displayName
55921      * @type String
55922      */
55923     displayName: null
55924 };
55925
55926 /**
55927  * @class Ext.chart.CartesianSeries
55928  * @extends Ext.chart.Series
55929  * CartesianSeries class for the charts widget.
55930  * @constructor
55931  */
55932 Ext.chart.CartesianSeries = Ext.extend(Ext.chart.Series, {
55933     /**
55934      * The field used to access the x-axis value from the items from the data
55935      * source.
55936      *
55937      * @property xField
55938      * @type String
55939      */
55940     xField: null,
55941
55942     /**
55943      * The field used to access the y-axis value from the items from the data
55944      * source.
55945      *
55946      * @property yField
55947      * @type String
55948      */
55949     yField: null,
55950
55951     /**
55952      * False to not show this series in the legend. Defaults to <tt>true</tt>.
55953      *
55954      * @property showInLegend
55955      * @type Boolean
55956      */
55957     showInLegend: true,
55958
55959     /**
55960      * Indicates which axis the series will bind to
55961      *
55962      * @property axis
55963      * @type String
55964      */
55965     axis: 'primary'
55966 });
55967
55968 /**
55969  * @class Ext.chart.ColumnSeries
55970  * @extends Ext.chart.CartesianSeries
55971  * ColumnSeries class for the charts widget.
55972  * @constructor
55973  */
55974 Ext.chart.ColumnSeries = Ext.extend(Ext.chart.CartesianSeries, {
55975     type: "column"
55976 });
55977
55978 /**
55979  * @class Ext.chart.LineSeries
55980  * @extends Ext.chart.CartesianSeries
55981  * LineSeries class for the charts widget.
55982  * @constructor
55983  */
55984 Ext.chart.LineSeries = Ext.extend(Ext.chart.CartesianSeries, {
55985     type: "line"
55986 });
55987
55988 /**
55989  * @class Ext.chart.BarSeries
55990  * @extends Ext.chart.CartesianSeries
55991  * BarSeries class for the charts widget.
55992  * @constructor
55993  */
55994 Ext.chart.BarSeries = Ext.extend(Ext.chart.CartesianSeries, {
55995     type: "bar"
55996 });
55997
55998
55999 /**
56000  * @class Ext.chart.PieSeries
56001  * @extends Ext.chart.Series
56002  * PieSeries class for the charts widget.
56003  * @constructor
56004  */
56005 Ext.chart.PieSeries = Ext.extend(Ext.chart.Series, {
56006     type: "pie",
56007     dataField: null,
56008     categoryField: null
56009 });/**
56010  * @class Ext.menu.Menu
56011  * @extends Ext.Container
56012  * <p>A menu object.  This is the container to which you may add menu items.  Menu can also serve as a base class
56013  * when you want a specialized menu based off of another component (like {@link Ext.menu.DateMenu} for example).</p>
56014  * <p>Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Component}s.</p>
56015  * <p>To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items}
56016  * specify <tt>iconCls: 'no-icon'</tt>.  This reserves a space for an icon, and indents the Component in line
56017  * with the other menu items.  See {@link Ext.form.ComboBox}.{@link Ext.form.ComboBox#getListParent getListParent}
56018  * for an example.</p>
56019  * <p>By default, Menus are absolutely positioned, floating Components. By configuring a Menu with
56020  * <b><tt>{@link #floating}:false</tt></b>, a Menu may be used as child of a Container.</p>
56021  *
56022  * @xtype menu
56023  */
56024 Ext.menu.Menu = Ext.extend(Ext.Container, {
56025     /**
56026      * @cfg {Object} defaults
56027      * A config object that will be applied to all items added to this container either via the {@link #items}
56028      * config or via the {@link #add} method.  The defaults config can contain any number of
56029      * name/value property pairs to be added to each item, and should be valid for the types of items
56030      * being added to the menu.
56031      */
56032     /**
56033      * @cfg {Mixed} items
56034      * An array of items to be added to this menu. Menus may contain either {@link Ext.menu.Item menu items},
56035      * or general {@link Ext.Component Component}s.
56036      */
56037     /**
56038      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
56039      */
56040     minWidth : 120,
56041     /**
56042      * @cfg {Boolean/String} shadow True or 'sides' for the default effect, 'frame' for 4-way shadow, and 'drop'
56043      * for bottom-right shadow (defaults to 'sides')
56044      */
56045     shadow : 'sides',
56046     /**
56047      * @cfg {String} subMenuAlign The {@link Ext.Element#alignTo} anchor position value to use for submenus of
56048      * this menu (defaults to 'tl-tr?')
56049      */
56050     subMenuAlign : 'tl-tr?',
56051     /**
56052      * @cfg {String} defaultAlign The default {@link Ext.Element#alignTo} anchor position value for this menu
56053      * relative to its element of origin (defaults to 'tl-bl?')
56054      */
56055     defaultAlign : 'tl-bl?',
56056     /**
56057      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
56058      */
56059     allowOtherMenus : false,
56060     /**
56061      * @cfg {Boolean} ignoreParentClicks True to ignore clicks on any item in this menu that is a parent item (displays
56062      * a submenu) so that the submenu is not dismissed when clicking the parent item (defaults to false).
56063      */
56064     ignoreParentClicks : false,
56065     /**
56066      * @cfg {Boolean} enableScrolling True to allow the menu container to have scroller controls if the menu is too long (defaults to true).
56067      */
56068     enableScrolling : true,
56069     /**
56070      * @cfg {Number} maxHeight The maximum height of the menu. Only applies when enableScrolling is set to True (defaults to null).
56071      */
56072     maxHeight : null,
56073     /**
56074      * @cfg {Number} scrollIncrement The amount to scroll the menu. Only applies when enableScrolling is set to True (defaults to 24).
56075      */
56076     scrollIncrement : 24,
56077     /**
56078      * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
56079      */
56080     showSeparator : true,
56081     /**
56082      * @cfg {Array} defaultOffsets An array specifying the [x, y] offset in pixels by which to
56083      * change the default Menu popup position after aligning according to the {@link #defaultAlign}
56084      * configuration. Defaults to <tt>[0, 0]</tt>.
56085      */
56086     defaultOffsets : [0, 0],
56087
56088     /**
56089      * @cfg {Boolean} plain
56090      * True to remove the incised line down the left side of the menu. Defaults to <tt>false</tt>.
56091      */
56092     plain : false,
56093
56094     /**
56095      * @cfg {Boolean} floating
56096      * <p>By default, a Menu configured as <b><code>floating:true</code></b>
56097      * will be rendered as an {@link Ext.Layer} (an absolutely positioned,
56098      * floating Component with zindex=15000).
56099      * If configured as <b><code>floating:false</code></b>, the Menu may be
56100      * used as child item of another Container instead of a free-floating
56101      * {@link Ext.Layer Layer}.
56102      */
56103     floating : true,
56104
56105
56106     /**
56107      * @cfg {Number} zIndex
56108      * zIndex to use when the menu is floating.
56109      */
56110     zIndex: 15000,
56111
56112     // private
56113     hidden : true,
56114
56115     /**
56116      * @cfg {String/Object} layout
56117      * This class assigns a default layout (<code>layout:'<b>menu</b>'</code>).
56118      * Developers <i>may</i> override this configuration option if another layout is required.
56119      * See {@link Ext.Container#layout} for additional information.
56120      */
56121     layout : 'menu',
56122     hideMode : 'offsets',    // Important for laying out Components
56123     scrollerHeight : 8,
56124     autoLayout : true,       // Provided for backwards compat
56125     defaultType : 'menuitem',
56126     bufferResize : false,
56127
56128     initComponent : function(){
56129         if(Ext.isArray(this.initialConfig)){
56130             Ext.apply(this, {items:this.initialConfig});
56131         }
56132         this.addEvents(
56133             /**
56134              * @event click
56135              * Fires when this menu is clicked (or when the enter key is pressed while it is active)
56136              * @param {Ext.menu.Menu} this
56137             * @param {Ext.menu.Item} menuItem The menu item that was clicked
56138              * @param {Ext.EventObject} e
56139              */
56140             'click',
56141             /**
56142              * @event mouseover
56143              * Fires when the mouse is hovering over this menu
56144              * @param {Ext.menu.Menu} this
56145              * @param {Ext.EventObject} e
56146              * @param {Ext.menu.Item} menuItem The menu item that was clicked
56147              */
56148             'mouseover',
56149             /**
56150              * @event mouseout
56151              * Fires when the mouse exits this menu
56152              * @param {Ext.menu.Menu} this
56153              * @param {Ext.EventObject} e
56154              * @param {Ext.menu.Item} menuItem The menu item that was clicked
56155              */
56156             'mouseout',
56157             /**
56158              * @event itemclick
56159              * Fires when a menu item contained in this menu is clicked
56160              * @param {Ext.menu.BaseItem} baseItem The BaseItem that was clicked
56161              * @param {Ext.EventObject} e
56162              */
56163             'itemclick'
56164         );
56165         Ext.menu.MenuMgr.register(this);
56166         if(this.floating){
56167             Ext.EventManager.onWindowResize(this.hide, this);
56168         }else{
56169             if(this.initialConfig.hidden !== false){
56170                 this.hidden = false;
56171             }
56172             this.internalDefaults = {hideOnClick: false};
56173         }
56174         Ext.menu.Menu.superclass.initComponent.call(this);
56175         if(this.autoLayout){
56176             var fn = this.doLayout.createDelegate(this, []);
56177             this.on({
56178                 add: fn,
56179                 remove: fn
56180             });
56181         }
56182     },
56183
56184     //private
56185     getLayoutTarget : function() {
56186         return this.ul;
56187     },
56188
56189     // private
56190     onRender : function(ct, position){
56191         if(!ct){
56192             ct = Ext.getBody();
56193         }
56194
56195         var dh = {
56196             id: this.getId(),
56197             cls: 'x-menu ' + ((this.floating) ? 'x-menu-floating x-layer ' : '') + (this.cls || '') + (this.plain ? ' x-menu-plain' : '') + (this.showSeparator ? '' : ' x-menu-nosep'),
56198             style: this.style,
56199             cn: [
56200                 {tag: 'a', cls: 'x-menu-focus', href: '#', onclick: 'return false;', tabIndex: '-1'},
56201                 {tag: 'ul', cls: 'x-menu-list'}
56202             ]
56203         };
56204         if(this.floating){
56205             this.el = new Ext.Layer({
56206                 shadow: this.shadow,
56207                 dh: dh,
56208                 constrain: false,
56209                 parentEl: ct,
56210                 zindex: this.zIndex
56211             });
56212         }else{
56213             this.el = ct.createChild(dh);
56214         }
56215         Ext.menu.Menu.superclass.onRender.call(this, ct, position);
56216
56217         if(!this.keyNav){
56218             this.keyNav = new Ext.menu.MenuNav(this);
56219         }
56220         // generic focus element
56221         this.focusEl = this.el.child('a.x-menu-focus');
56222         this.ul = this.el.child('ul.x-menu-list');
56223         this.mon(this.ul, {
56224             scope: this,
56225             click: this.onClick,
56226             mouseover: this.onMouseOver,
56227             mouseout: this.onMouseOut
56228         });
56229         if(this.enableScrolling){
56230             this.mon(this.el, {
56231                 scope: this,
56232                 delegate: '.x-menu-scroller',
56233                 click: this.onScroll,
56234                 mouseover: this.deactivateActive
56235             });
56236         }
56237     },
56238
56239     // private
56240     findTargetItem : function(e){
56241         var t = e.getTarget('.x-menu-list-item', this.ul, true);
56242         if(t && t.menuItemId){
56243             return this.items.get(t.menuItemId);
56244         }
56245     },
56246
56247     // private
56248     onClick : function(e){
56249         var t = this.findTargetItem(e);
56250         if(t){
56251             if(t.isFormField){
56252                 this.setActiveItem(t);
56253             }else if(t instanceof Ext.menu.BaseItem){
56254                 if(t.menu && this.ignoreParentClicks){
56255                     t.expandMenu();
56256                     e.preventDefault();
56257                 }else if(t.onClick){
56258                     t.onClick(e);
56259                     this.fireEvent('click', this, t, e);
56260                 }
56261             }
56262         }
56263     },
56264
56265     // private
56266     setActiveItem : function(item, autoExpand){
56267         if(item != this.activeItem){
56268             this.deactivateActive();
56269             if((this.activeItem = item).isFormField){
56270                 item.focus();
56271             }else{
56272                 item.activate(autoExpand);
56273             }
56274         }else if(autoExpand){
56275             item.expandMenu();
56276         }
56277     },
56278
56279     deactivateActive : function(){
56280         var a = this.activeItem;
56281         if(a){
56282             if(a.isFormField){
56283                 //Fields cannot deactivate, but Combos must collapse
56284                 if(a.collapse){
56285                     a.collapse();
56286                 }
56287             }else{
56288                 a.deactivate();
56289             }
56290             delete this.activeItem;
56291         }
56292     },
56293
56294     // private
56295     tryActivate : function(start, step){
56296         var items = this.items;
56297         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
56298             var item = items.get(i);
56299             if(!item.disabled && (item.canActivate || item.isFormField)){
56300                 this.setActiveItem(item, false);
56301                 return item;
56302             }
56303         }
56304         return false;
56305     },
56306
56307     // private
56308     onMouseOver : function(e){
56309         var t = this.findTargetItem(e);
56310         if(t){
56311             if(t.canActivate && !t.disabled){
56312                 this.setActiveItem(t, true);
56313             }
56314         }
56315         this.over = true;
56316         this.fireEvent('mouseover', this, e, t);
56317     },
56318
56319     // private
56320     onMouseOut : function(e){
56321         var t = this.findTargetItem(e);
56322         if(t){
56323             if(t == this.activeItem && t.shouldDeactivate && t.shouldDeactivate(e)){
56324                 this.activeItem.deactivate();
56325                 delete this.activeItem;
56326             }
56327         }
56328         this.over = false;
56329         this.fireEvent('mouseout', this, e, t);
56330     },
56331
56332     // private
56333     onScroll : function(e, t){
56334         if(e){
56335             e.stopEvent();
56336         }
56337         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
56338         ul.scrollTop += this.scrollIncrement * (top ? -1 : 1);
56339         if(top ? ul.scrollTop <= 0 : ul.scrollTop + this.activeMax >= ul.scrollHeight){
56340            this.onScrollerOut(null, t);
56341         }
56342     },
56343
56344     // private
56345     onScrollerIn : function(e, t){
56346         var ul = this.ul.dom, top = Ext.fly(t).is('.x-menu-scroller-top');
56347         if(top ? ul.scrollTop > 0 : ul.scrollTop + this.activeMax < ul.scrollHeight){
56348             Ext.fly(t).addClass(['x-menu-item-active', 'x-menu-scroller-active']);
56349         }
56350     },
56351
56352     // private
56353     onScrollerOut : function(e, t){
56354         Ext.fly(t).removeClass(['x-menu-item-active', 'x-menu-scroller-active']);
56355     },
56356
56357     /**
56358      * If <code>{@link #floating}=true</code>, shows this menu relative to
56359      * another element using {@link #showat}, otherwise uses {@link Ext.Component#show}.
56360      * @param {Mixed} element The element to align to
56361      * @param {String} position (optional) The {@link Ext.Element#alignTo} anchor position to use in aligning to
56362      * the element (defaults to this.defaultAlign)
56363      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
56364      */
56365     show : function(el, pos, parentMenu){
56366         if(this.floating){
56367             this.parentMenu = parentMenu;
56368             if(!this.el){
56369                 this.render();
56370                 this.doLayout(false, true);
56371             }
56372             this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign, this.defaultOffsets), parentMenu);
56373         }else{
56374             Ext.menu.Menu.superclass.show.call(this);
56375         }
56376     },
56377
56378     /**
56379      * Displays this menu at a specific xy position and fires the 'show' event if a
56380      * handler for the 'beforeshow' event does not return false cancelling the operation.
56381      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
56382      * @param {Ext.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
56383      */
56384     showAt : function(xy, parentMenu){
56385         if(this.fireEvent('beforeshow', this) !== false){
56386             this.parentMenu = parentMenu;
56387             if(!this.el){
56388                 this.render();
56389             }
56390             if(this.enableScrolling){
56391                 // set the position so we can figure out the constrain value.
56392                 this.el.setXY(xy);
56393                 //constrain the value, keep the y coordinate the same
56394                 xy[1] = this.constrainScroll(xy[1]);
56395                 xy = [this.el.adjustForConstraints(xy)[0], xy[1]];
56396             }else{
56397                 //constrain to the viewport.
56398                 xy = this.el.adjustForConstraints(xy);
56399             }
56400             this.el.setXY(xy);
56401             this.el.show();
56402             Ext.menu.Menu.superclass.onShow.call(this);
56403             if(Ext.isIE){
56404                 // internal event, used so we don't couple the layout to the menu
56405                 this.fireEvent('autosize', this);
56406                 if(!Ext.isIE8){
56407                     this.el.repaint();
56408                 }
56409             }
56410             this.hidden = false;
56411             this.focus();
56412             this.fireEvent('show', this);
56413         }
56414     },
56415
56416     constrainScroll : function(y){
56417         var max, full = this.ul.setHeight('auto').getHeight(),
56418             returnY = y, normalY, parentEl, scrollTop, viewHeight;
56419         if(this.floating){
56420             parentEl = Ext.fly(this.el.dom.parentNode);
56421             scrollTop = parentEl.getScroll().top;
56422             viewHeight = parentEl.getViewSize().height;
56423             //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
56424             //of the view.
56425             normalY = y - scrollTop;
56426             max = this.maxHeight ? this.maxHeight : viewHeight - normalY;
56427             if(full > viewHeight) {
56428                 max = viewHeight;
56429                 //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
56430                 returnY = y - normalY;
56431             } else if(max < full) {
56432                 returnY = y - (full - max);
56433                 max = full;
56434             }
56435         }else{
56436             max = this.getHeight();
56437         }
56438         // Always respect maxHeight 
56439         if (this.maxHeight){
56440             max = Math.min(this.maxHeight, max);
56441         }
56442         if(full > max && max > 0){
56443             this.activeMax = max - this.scrollerHeight * 2 - this.el.getFrameWidth('tb') - Ext.num(this.el.shadowOffset, 0);
56444             this.ul.setHeight(this.activeMax);
56445             this.createScrollers();
56446             this.el.select('.x-menu-scroller').setDisplayed('');
56447         }else{
56448             this.ul.setHeight(full);
56449             this.el.select('.x-menu-scroller').setDisplayed('none');
56450         }
56451         this.ul.dom.scrollTop = 0;
56452         return returnY;
56453     },
56454
56455     createScrollers : function(){
56456         if(!this.scroller){
56457             this.scroller = {
56458                 pos: 0,
56459                 top: this.el.insertFirst({
56460                     tag: 'div',
56461                     cls: 'x-menu-scroller x-menu-scroller-top',
56462                     html: '&#160;'
56463                 }),
56464                 bottom: this.el.createChild({
56465                     tag: 'div',
56466                     cls: 'x-menu-scroller x-menu-scroller-bottom',
56467                     html: '&#160;'
56468                 })
56469             };
56470             this.scroller.top.hover(this.onScrollerIn, this.onScrollerOut, this);
56471             this.scroller.topRepeater = new Ext.util.ClickRepeater(this.scroller.top, {
56472                 listeners: {
56473                     click: this.onScroll.createDelegate(this, [null, this.scroller.top], false)
56474                 }
56475             });
56476             this.scroller.bottom.hover(this.onScrollerIn, this.onScrollerOut, this);
56477             this.scroller.bottomRepeater = new Ext.util.ClickRepeater(this.scroller.bottom, {
56478                 listeners: {
56479                     click: this.onScroll.createDelegate(this, [null, this.scroller.bottom], false)
56480                 }
56481             });
56482         }
56483     },
56484
56485     onLayout : function(){
56486         if(this.isVisible()){
56487             if(this.enableScrolling){
56488                 this.constrainScroll(this.el.getTop());
56489             }
56490             if(this.floating){
56491                 this.el.sync();
56492             }
56493         }
56494     },
56495
56496     focus : function(){
56497         if(!this.hidden){
56498             this.doFocus.defer(50, this);
56499         }
56500     },
56501
56502     doFocus : function(){
56503         if(!this.hidden){
56504             this.focusEl.focus();
56505         }
56506     },
56507
56508     /**
56509      * Hides this menu and optionally all parent menus
56510      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
56511      */
56512     hide : function(deep){
56513         if (!this.isDestroyed) {
56514             this.deepHide = deep;
56515             Ext.menu.Menu.superclass.hide.call(this);
56516             delete this.deepHide;
56517         }
56518     },
56519
56520     // private
56521     onHide : function(){
56522         Ext.menu.Menu.superclass.onHide.call(this);
56523         this.deactivateActive();
56524         if(this.el && this.floating){
56525             this.el.hide();
56526         }
56527         var pm = this.parentMenu;
56528         if(this.deepHide === true && pm){
56529             if(pm.floating){
56530                 pm.hide(true);
56531             }else{
56532                 pm.deactivateActive();
56533             }
56534         }
56535     },
56536
56537     // private
56538     lookupComponent : function(c){
56539          if(Ext.isString(c)){
56540             c = (c == 'separator' || c == '-') ? new Ext.menu.Separator() : new Ext.menu.TextItem(c);
56541              this.applyDefaults(c);
56542          }else{
56543             if(Ext.isObject(c)){
56544                 c = this.getMenuItem(c);
56545             }else if(c.tagName || c.el){ // element. Wrap it.
56546                 c = new Ext.BoxComponent({
56547                     el: c
56548                 });
56549             }
56550          }
56551          return c;
56552     },
56553
56554     applyDefaults : function(c){
56555         if(!Ext.isString(c)){
56556             c = Ext.menu.Menu.superclass.applyDefaults.call(this, c);
56557             var d = this.internalDefaults;
56558             if(d){
56559                 if(c.events){
56560                     Ext.applyIf(c.initialConfig, d);
56561                     Ext.apply(c, d);
56562                 }else{
56563                     Ext.applyIf(c, d);
56564                 }
56565             }
56566         }
56567         return c;
56568     },
56569
56570     // private
56571     getMenuItem : function(config){
56572        if(!config.isXType){
56573             if(!config.xtype && Ext.isBoolean(config.checked)){
56574                 return new Ext.menu.CheckItem(config)
56575             }
56576             return Ext.create(config, this.defaultType);
56577         }
56578         return config;
56579     },
56580
56581     /**
56582      * Adds a separator bar to the menu
56583      * @return {Ext.menu.Item} The menu item that was added
56584      */
56585     addSeparator : function(){
56586         return this.add(new Ext.menu.Separator());
56587     },
56588
56589     /**
56590      * Adds an {@link Ext.Element} object to the menu
56591      * @param {Mixed} el The element or DOM node to add, or its id
56592      * @return {Ext.menu.Item} The menu item that was added
56593      */
56594     addElement : function(el){
56595         return this.add(new Ext.menu.BaseItem({
56596             el: el
56597         }));
56598     },
56599
56600     /**
56601      * Adds an existing object based on {@link Ext.menu.BaseItem} to the menu
56602      * @param {Ext.menu.Item} item The menu item to add
56603      * @return {Ext.menu.Item} The menu item that was added
56604      */
56605     addItem : function(item){
56606         return this.add(item);
56607     },
56608
56609     /**
56610      * Creates a new {@link Ext.menu.Item} based an the supplied config object and adds it to the menu
56611      * @param {Object} config A MenuItem config object
56612      * @return {Ext.menu.Item} The menu item that was added
56613      */
56614     addMenuItem : function(config){
56615         return this.add(this.getMenuItem(config));
56616     },
56617
56618     /**
56619      * Creates a new {@link Ext.menu.TextItem} with the supplied text and adds it to the menu
56620      * @param {String} text The text to display in the menu item
56621      * @return {Ext.menu.Item} The menu item that was added
56622      */
56623     addText : function(text){
56624         return this.add(new Ext.menu.TextItem(text));
56625     },
56626
56627     //private
56628     onDestroy : function(){
56629         Ext.EventManager.removeResizeListener(this.hide, this);
56630         var pm = this.parentMenu;
56631         if(pm && pm.activeChild == this){
56632             delete pm.activeChild;
56633         }
56634         delete this.parentMenu;
56635         Ext.menu.Menu.superclass.onDestroy.call(this);
56636         Ext.menu.MenuMgr.unregister(this);
56637         if(this.keyNav) {
56638             this.keyNav.disable();
56639         }
56640         var s = this.scroller;
56641         if(s){
56642             Ext.destroy(s.topRepeater, s.bottomRepeater, s.top, s.bottom);
56643         }
56644         Ext.destroy(
56645             this.el,
56646             this.focusEl,
56647             this.ul
56648         );
56649     }
56650 });
56651
56652 Ext.reg('menu', Ext.menu.Menu);
56653
56654 // MenuNav is a private utility class used internally by the Menu
56655 Ext.menu.MenuNav = Ext.extend(Ext.KeyNav, function(){
56656     function up(e, m){
56657         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
56658             m.tryActivate(m.items.length-1, -1);
56659         }
56660     }
56661     function down(e, m){
56662         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
56663             m.tryActivate(0, 1);
56664         }
56665     }
56666     return {
56667         constructor : function(menu){
56668             Ext.menu.MenuNav.superclass.constructor.call(this, menu.el);
56669             this.scope = this.menu = menu;
56670         },
56671
56672         doRelay : function(e, h){
56673             var k = e.getKey();
56674 //          Keystrokes within a form Field (e.g.: down in a Combo) do not navigate. Allow only TAB
56675             if (this.menu.activeItem && this.menu.activeItem.isFormField && k != e.TAB) {
56676                 return false;
56677             }
56678             if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
56679                 this.menu.tryActivate(0, 1);
56680                 return false;
56681             }
56682             return h.call(this.scope || this, e, this.menu);
56683         },
56684
56685         tab: function(e, m) {
56686             e.stopEvent();
56687             if (e.shiftKey) {
56688                 up(e, m);
56689             } else {
56690                 down(e, m);
56691             }
56692         },
56693
56694         up : up,
56695
56696         down : down,
56697
56698         right : function(e, m){
56699             if(m.activeItem){
56700                 m.activeItem.expandMenu(true);
56701             }
56702         },
56703
56704         left : function(e, m){
56705             m.hide();
56706             if(m.parentMenu && m.parentMenu.activeItem){
56707                 m.parentMenu.activeItem.activate();
56708             }
56709         },
56710
56711         enter : function(e, m){
56712             if(m.activeItem){
56713                 e.stopPropagation();
56714                 m.activeItem.onClick(e);
56715                 m.fireEvent('click', this, m.activeItem);
56716                 return true;
56717             }
56718         }
56719     };
56720 }());
56721 /**
56722  * @class Ext.menu.MenuMgr
56723  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
56724  * @singleton
56725  */
56726 Ext.menu.MenuMgr = function(){
56727    var menus, active, groups = {}, attached = false, lastShow = new Date();
56728
56729    // private - called when first menu is created
56730    function init(){
56731        menus = {};
56732        active = new Ext.util.MixedCollection();
56733        Ext.getDoc().addKeyListener(27, function(){
56734            if(active.length > 0){
56735                hideAll();
56736            }
56737        });
56738    }
56739
56740    // private
56741    function hideAll(){
56742        if(active && active.length > 0){
56743            var c = active.clone();
56744            c.each(function(m){
56745                m.hide();
56746            });
56747            return true;
56748        }
56749        return false;
56750    }
56751
56752    // private
56753    function onHide(m){
56754        active.remove(m);
56755        if(active.length < 1){
56756            Ext.getDoc().un("mousedown", onMouseDown);
56757            attached = false;
56758        }
56759    }
56760
56761    // private
56762    function onShow(m){
56763        var last = active.last();
56764        lastShow = new Date();
56765        active.add(m);
56766        if(!attached){
56767            Ext.getDoc().on("mousedown", onMouseDown);
56768            attached = true;
56769        }
56770        if(m.parentMenu){
56771           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
56772           m.parentMenu.activeChild = m;
56773        }else if(last && !last.isDestroyed && last.isVisible()){
56774           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
56775        }
56776    }
56777
56778    // private
56779    function onBeforeHide(m){
56780        if(m.activeChild){
56781            m.activeChild.hide();
56782        }
56783        if(m.autoHideTimer){
56784            clearTimeout(m.autoHideTimer);
56785            delete m.autoHideTimer;
56786        }
56787    }
56788
56789    // private
56790    function onBeforeShow(m){
56791        var pm = m.parentMenu;
56792        if(!pm && !m.allowOtherMenus){
56793            hideAll();
56794        }else if(pm && pm.activeChild){
56795            pm.activeChild.hide();
56796        }
56797    }
56798
56799    // private
56800    function onMouseDown(e){
56801        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
56802            hideAll();
56803        }
56804    }
56805
56806    // private
56807    function onBeforeCheck(mi, state){
56808        if(state){
56809            var g = groups[mi.group];
56810            for(var i = 0, l = g.length; i < l; i++){
56811                if(g[i] != mi){
56812                    g[i].setChecked(false);
56813                }
56814            }
56815        }
56816    }
56817
56818    return {
56819
56820        /**
56821         * Hides all menus that are currently visible
56822         * @return {Boolean} success True if any active menus were hidden.
56823         */
56824        hideAll : function(){
56825             return hideAll();
56826        },
56827
56828        // private
56829        register : function(menu){
56830            if(!menus){
56831                init();
56832            }
56833            menus[menu.id] = menu;
56834            menu.on({
56835                beforehide: onBeforeHide,
56836                hide: onHide,
56837                beforeshow: onBeforeShow,
56838                show: onShow
56839            });
56840        },
56841
56842         /**
56843          * Returns a {@link Ext.menu.Menu} object
56844          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
56845          * be used to generate and return a new Menu instance.
56846          * @return {Ext.menu.Menu} The specified menu, or null if none are found
56847          */
56848        get : function(menu){
56849            if(typeof menu == "string"){ // menu id
56850                if(!menus){  // not initialized, no menus to return
56851                    return null;
56852                }
56853                return menus[menu];
56854            }else if(menu.events){  // menu instance
56855                return menu;
56856            }else if(typeof menu.length == 'number'){ // array of menu items?
56857                return new Ext.menu.Menu({items:menu});
56858            }else{ // otherwise, must be a config
56859                return Ext.create(menu, 'menu');
56860            }
56861        },
56862
56863        // private
56864        unregister : function(menu){
56865            delete menus[menu.id];
56866            menu.un("beforehide", onBeforeHide);
56867            menu.un("hide", onHide);
56868            menu.un("beforeshow", onBeforeShow);
56869            menu.un("show", onShow);
56870        },
56871
56872        // private
56873        registerCheckable : function(menuItem){
56874            var g = menuItem.group;
56875            if(g){
56876                if(!groups[g]){
56877                    groups[g] = [];
56878                }
56879                groups[g].push(menuItem);
56880                menuItem.on("beforecheckchange", onBeforeCheck);
56881            }
56882        },
56883
56884        // private
56885        unregisterCheckable : function(menuItem){
56886            var g = menuItem.group;
56887            if(g){
56888                groups[g].remove(menuItem);
56889                menuItem.un("beforecheckchange", onBeforeCheck);
56890            }
56891        },
56892
56893        getCheckedItem : function(groupId){
56894            var g = groups[groupId];
56895            if(g){
56896                for(var i = 0, l = g.length; i < l; i++){
56897                    if(g[i].checked){
56898                        return g[i];
56899                    }
56900                }
56901            }
56902            return null;
56903        },
56904
56905        setCheckedItem : function(groupId, itemId){
56906            var g = groups[groupId];
56907            if(g){
56908                for(var i = 0, l = g.length; i < l; i++){
56909                    if(g[i].id == itemId){
56910                        g[i].setChecked(true);
56911                    }
56912                }
56913            }
56914            return null;
56915        }
56916    };
56917 }();
56918 /**
56919  * @class Ext.menu.BaseItem
56920  * @extends Ext.Component
56921  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
56922  * management and base configuration options shared by all menu components.
56923  * @constructor
56924  * Creates a new BaseItem
56925  * @param {Object} config Configuration options
56926  * @xtype menubaseitem
56927  */
56928 Ext.menu.BaseItem = Ext.extend(Ext.Component, {
56929     /**
56930      * @property parentMenu
56931      * @type Ext.menu.Menu
56932      * The parent Menu of this Item.
56933      */
56934     /**
56935      * @cfg {Function} handler
56936      * A function that will handle the click event of this menu item (optional).
56937      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
56938      * <li><code>b</code> : Item<div class="sub-desc">This menu Item.</div></li>
56939      * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
56940      * </ul></div>
56941      */
56942     /**
56943      * @cfg {Object} scope
56944      * The scope (<tt><b>this</b></tt> reference) in which the handler function will be called.
56945      */
56946     /**
56947      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
56948      */
56949     canActivate : false,
56950     /**
56951      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
56952      */
56953     activeClass : "x-menu-item-active",
56954     /**
56955      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
56956      */
56957     hideOnClick : true,
56958     /**
56959      * @cfg {Number} clickHideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 1)
56960      */
56961     clickHideDelay : 1,
56962
56963     // private
56964     ctype : "Ext.menu.BaseItem",
56965
56966     // private
56967     actionMode : "container",
56968
56969     initComponent : function(){
56970         Ext.menu.BaseItem.superclass.initComponent.call(this);
56971         this.addEvents(
56972             /**
56973              * @event click
56974              * Fires when this item is clicked
56975              * @param {Ext.menu.BaseItem} this
56976              * @param {Ext.EventObject} e
56977              */
56978             'click',
56979             /**
56980              * @event activate
56981              * Fires when this item is activated
56982              * @param {Ext.menu.BaseItem} this
56983              */
56984             'activate',
56985             /**
56986              * @event deactivate
56987              * Fires when this item is deactivated
56988              * @param {Ext.menu.BaseItem} this
56989              */
56990             'deactivate'
56991         );
56992         if(this.handler){
56993             this.on("click", this.handler, this.scope);
56994         }
56995     },
56996
56997     // private
56998     onRender : function(container, position){
56999         Ext.menu.BaseItem.superclass.onRender.apply(this, arguments);
57000         if(this.ownerCt && this.ownerCt instanceof Ext.menu.Menu){
57001             this.parentMenu = this.ownerCt;
57002         }else{
57003             this.container.addClass('x-menu-list-item');
57004             this.mon(this.el, {
57005                 scope: this,
57006                 click: this.onClick,
57007                 mouseenter: this.activate,
57008                 mouseleave: this.deactivate
57009             });
57010         }
57011     },
57012
57013     /**
57014      * Sets the function that will handle click events for this item (equivalent to passing in the {@link #handler}
57015      * config property).  If an existing handler is already registered, it will be unregistered for you.
57016      * @param {Function} handler The function that should be called on click
57017      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function is executed. Defaults to this menu item.
57018      */
57019     setHandler : function(handler, scope){
57020         if(this.handler){
57021             this.un("click", this.handler, this.scope);
57022         }
57023         this.on("click", this.handler = handler, this.scope = scope);
57024     },
57025
57026     // private
57027     onClick : function(e){
57028         if(!this.disabled && this.fireEvent("click", this, e) !== false
57029                 && (this.parentMenu && this.parentMenu.fireEvent("itemclick", this, e) !== false)){
57030             this.handleClick(e);
57031         }else{
57032             e.stopEvent();
57033         }
57034     },
57035
57036     // private
57037     activate : function(){
57038         if(this.disabled){
57039             return false;
57040         }
57041         var li = this.container;
57042         li.addClass(this.activeClass);
57043         this.region = li.getRegion().adjust(2, 2, -2, -2);
57044         this.fireEvent("activate", this);
57045         return true;
57046     },
57047
57048     // private
57049     deactivate : function(){
57050         this.container.removeClass(this.activeClass);
57051         this.fireEvent("deactivate", this);
57052     },
57053
57054     // private
57055     shouldDeactivate : function(e){
57056         return !this.region || !this.region.contains(e.getPoint());
57057     },
57058
57059     // private
57060     handleClick : function(e){
57061         var pm = this.parentMenu;
57062         if(this.hideOnClick){
57063             if(pm.floating){
57064                 pm.hide.defer(this.clickHideDelay, pm, [true]);
57065             }else{
57066                 pm.deactivateActive();
57067             }
57068         }
57069     },
57070
57071     // private. Do nothing
57072     expandMenu : Ext.emptyFn,
57073
57074     // private. Do nothing
57075     hideMenu : Ext.emptyFn
57076 });
57077 Ext.reg('menubaseitem', Ext.menu.BaseItem);/**
57078  * @class Ext.menu.TextItem
57079  * @extends Ext.menu.BaseItem
57080  * Adds a static text string to a menu, usually used as either a heading or group separator.
57081  * @constructor
57082  * Creates a new TextItem
57083  * @param {Object/String} config If config is a string, it is used as the text to display, otherwise it
57084  * is applied as a config object (and should contain a <tt>text</tt> property).
57085  * @xtype menutextitem
57086  */
57087 Ext.menu.TextItem = Ext.extend(Ext.menu.BaseItem, {
57088     /**
57089      * @cfg {String} text The text to display for this item (defaults to '')
57090      */
57091     /**
57092      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
57093      */
57094     hideOnClick : false,
57095     /**
57096      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
57097      */
57098     itemCls : "x-menu-text",
57099     
57100     constructor : function(config){
57101         if(typeof config == 'string'){
57102             config = {text: config}
57103         }
57104         Ext.menu.TextItem.superclass.constructor.call(this, config);
57105     },
57106
57107     // private
57108     onRender : function(){
57109         var s = document.createElement("span");
57110         s.className = this.itemCls;
57111         s.innerHTML = this.text;
57112         this.el = s;
57113         Ext.menu.TextItem.superclass.onRender.apply(this, arguments);
57114     }
57115 });
57116 Ext.reg('menutextitem', Ext.menu.TextItem);/**
57117  * @class Ext.menu.Separator
57118  * @extends Ext.menu.BaseItem
57119  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
57120  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
57121  * @constructor
57122  * @param {Object} config Configuration options
57123  * @xtype menuseparator
57124  */
57125 Ext.menu.Separator = Ext.extend(Ext.menu.BaseItem, {
57126     /**
57127      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
57128      */
57129     itemCls : "x-menu-sep",
57130     /**
57131      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
57132      */
57133     hideOnClick : false,
57134     
57135     /** 
57136      * @cfg {String} activeClass
57137      * @hide 
57138      */
57139     activeClass: '',
57140
57141     // private
57142     onRender : function(li){
57143         var s = document.createElement("span");
57144         s.className = this.itemCls;
57145         s.innerHTML = "&#160;";
57146         this.el = s;
57147         li.addClass("x-menu-sep-li");
57148         Ext.menu.Separator.superclass.onRender.apply(this, arguments);
57149     }
57150 });
57151 Ext.reg('menuseparator', Ext.menu.Separator);/**
57152  * @class Ext.menu.Item
57153  * @extends Ext.menu.BaseItem
57154  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
57155  * display items.  Item extends the base functionality of {@link Ext.menu.BaseItem} by adding menu-specific
57156  * activation and click handling.
57157  * @constructor
57158  * Creates a new Item
57159  * @param {Object} config Configuration options
57160  * @xtype menuitem
57161  */
57162 Ext.menu.Item = Ext.extend(Ext.menu.BaseItem, {
57163     /**
57164      * @property menu
57165      * @type Ext.menu.Menu
57166      * The submenu associated with this Item if one was configured.
57167      */
57168     /**
57169      * @cfg {Mixed} menu (optional) Either an instance of {@link Ext.menu.Menu} or the config object for an
57170      * {@link Ext.menu.Menu} which acts as the submenu when this item is activated.
57171      */
57172     /**
57173      * @cfg {String} icon The path to an icon to display in this item (defaults to Ext.BLANK_IMAGE_URL).  If
57174      * icon is specified {@link #iconCls} should not be.
57175      */
57176     /**
57177      * @cfg {String} iconCls A CSS class that specifies a background image that will be used as the icon for
57178      * this item (defaults to '').  If iconCls is specified {@link #icon} should not be.
57179      */
57180     /**
57181      * @cfg {String} text The text to display in this item (defaults to '').
57182      */
57183     /**
57184      * @cfg {String} href The href attribute to use for the underlying anchor link (defaults to '#').
57185      */
57186     /**
57187      * @cfg {String} hrefTarget The target attribute to use for the underlying anchor link (defaults to '').
57188      */
57189     /**
57190      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to 'x-menu-item')
57191      */
57192     itemCls : 'x-menu-item',
57193     /**
57194      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
57195      */
57196     canActivate : true,
57197     /**
57198      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
57199      */
57200     showDelay: 200,
57201     // doc'd in BaseItem
57202     hideDelay: 200,
57203
57204     // private
57205     ctype: 'Ext.menu.Item',
57206
57207     initComponent : function(){
57208         Ext.menu.Item.superclass.initComponent.call(this);
57209         if(this.menu){
57210             this.menu = Ext.menu.MenuMgr.get(this.menu);
57211             this.menu.ownerCt = this;
57212         }
57213     },
57214
57215     // private
57216     onRender : function(container, position){
57217         if (!this.itemTpl) {
57218             this.itemTpl = Ext.menu.Item.prototype.itemTpl = new Ext.XTemplate(
57219                 '<a id="{id}" class="{cls}" hidefocus="true" unselectable="on" href="{href}"',
57220                     '<tpl if="hrefTarget">',
57221                         ' target="{hrefTarget}"',
57222                     '</tpl>',
57223                  '>',
57224                      '<img src="{icon}" class="x-menu-item-icon {iconCls}"/>',
57225                      '<span class="x-menu-item-text">{text}</span>',
57226                  '</a>'
57227              );
57228         }
57229         var a = this.getTemplateArgs();
57230         this.el = position ? this.itemTpl.insertBefore(position, a, true) : this.itemTpl.append(container, a, true);
57231         this.iconEl = this.el.child('img.x-menu-item-icon');
57232         this.textEl = this.el.child('.x-menu-item-text');
57233         if(!this.href) { // if no link defined, prevent the default anchor event
57234             this.mon(this.el, 'click', Ext.emptyFn, null, { preventDefault: true });
57235         }
57236         Ext.menu.Item.superclass.onRender.call(this, container, position);
57237     },
57238
57239     getTemplateArgs: function() {
57240         return {
57241             id: this.id,
57242             cls: this.itemCls + (this.menu ?  ' x-menu-item-arrow' : '') + (this.cls ?  ' ' + this.cls : ''),
57243             href: this.href || '#',
57244             hrefTarget: this.hrefTarget,
57245             icon: this.icon || Ext.BLANK_IMAGE_URL,
57246             iconCls: this.iconCls || '',
57247             text: this.itemText||this.text||'&#160;'
57248         };
57249     },
57250
57251     /**
57252      * Sets the text to display in this menu item
57253      * @param {String} text The text to display
57254      */
57255     setText : function(text){
57256         this.text = text||'&#160;';
57257         if(this.rendered){
57258             this.textEl.update(this.text);
57259             this.parentMenu.layout.doAutoSize();
57260         }
57261     },
57262
57263     /**
57264      * Sets the CSS class to apply to the item's icon element
57265      * @param {String} cls The CSS class to apply
57266      */
57267     setIconClass : function(cls){
57268         var oldCls = this.iconCls;
57269         this.iconCls = cls;
57270         if(this.rendered){
57271             this.iconEl.replaceClass(oldCls, this.iconCls);
57272         }
57273     },
57274
57275     //private
57276     beforeDestroy: function(){
57277         if (this.menu){
57278             delete this.menu.ownerCt;
57279             this.menu.destroy();
57280         }
57281         Ext.menu.Item.superclass.beforeDestroy.call(this);
57282     },
57283
57284     // private
57285     handleClick : function(e){
57286         if(!this.href){ // if no link defined, stop the event automatically
57287             e.stopEvent();
57288         }
57289         Ext.menu.Item.superclass.handleClick.apply(this, arguments);
57290     },
57291
57292     // private
57293     activate : function(autoExpand){
57294         if(Ext.menu.Item.superclass.activate.apply(this, arguments)){
57295             this.focus();
57296             if(autoExpand){
57297                 this.expandMenu();
57298             }
57299         }
57300         return true;
57301     },
57302
57303     // private
57304     shouldDeactivate : function(e){
57305         if(Ext.menu.Item.superclass.shouldDeactivate.call(this, e)){
57306             if(this.menu && this.menu.isVisible()){
57307                 return !this.menu.getEl().getRegion().contains(e.getPoint());
57308             }
57309             return true;
57310         }
57311         return false;
57312     },
57313
57314     // private
57315     deactivate : function(){
57316         Ext.menu.Item.superclass.deactivate.apply(this, arguments);
57317         this.hideMenu();
57318     },
57319
57320     // private
57321     expandMenu : function(autoActivate){
57322         if(!this.disabled && this.menu){
57323             clearTimeout(this.hideTimer);
57324             delete this.hideTimer;
57325             if(!this.menu.isVisible() && !this.showTimer){
57326                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
57327             }else if (this.menu.isVisible() && autoActivate){
57328                 this.menu.tryActivate(0, 1);
57329             }
57330         }
57331     },
57332
57333     // private
57334     deferExpand : function(autoActivate){
57335         delete this.showTimer;
57336         this.menu.show(this.container, this.parentMenu.subMenuAlign || 'tl-tr?', this.parentMenu);
57337         if(autoActivate){
57338             this.menu.tryActivate(0, 1);
57339         }
57340     },
57341
57342     // private
57343     hideMenu : function(){
57344         clearTimeout(this.showTimer);
57345         delete this.showTimer;
57346         if(!this.hideTimer && this.menu && this.menu.isVisible()){
57347             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
57348         }
57349     },
57350
57351     // private
57352     deferHide : function(){
57353         delete this.hideTimer;
57354         if(this.menu.over){
57355             this.parentMenu.setActiveItem(this, false);
57356         }else{
57357             this.menu.hide();
57358         }
57359     }
57360 });
57361 Ext.reg('menuitem', Ext.menu.Item);/**
57362  * @class Ext.menu.CheckItem
57363  * @extends Ext.menu.Item
57364  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
57365  * @constructor
57366  * Creates a new CheckItem
57367  * @param {Object} config Configuration options
57368  * @xtype menucheckitem
57369  */
57370 Ext.menu.CheckItem = Ext.extend(Ext.menu.Item, {
57371     /**
57372      * @cfg {String} group
57373      * All check items with the same group name will automatically be grouped into a single-select
57374      * radio button group (defaults to '')
57375      */
57376     /**
57377      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
57378      */
57379     itemCls : "x-menu-item x-menu-check-item",
57380     /**
57381      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
57382      */
57383     groupClass : "x-menu-group-item",
57384
57385     /**
57386      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
57387      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
57388      * initialized with checked = true will be rendered as checked.
57389      */
57390     checked: false,
57391
57392     // private
57393     ctype: "Ext.menu.CheckItem",
57394     
57395     initComponent : function(){
57396         Ext.menu.CheckItem.superclass.initComponent.call(this);
57397             this.addEvents(
57398                 /**
57399                  * @event beforecheckchange
57400                  * Fires before the checked value is set, providing an opportunity to cancel if needed
57401                  * @param {Ext.menu.CheckItem} this
57402                  * @param {Boolean} checked The new checked value that will be set
57403                  */
57404                 "beforecheckchange" ,
57405                 /**
57406                  * @event checkchange
57407                  * Fires after the checked value has been set
57408                  * @param {Ext.menu.CheckItem} this
57409                  * @param {Boolean} checked The checked value that was set
57410                  */
57411                 "checkchange"
57412             );
57413             /**
57414              * A function that handles the checkchange event.  The function is undefined by default, but if an implementation
57415              * is provided, it will be called automatically when the checkchange event fires.
57416              * @param {Ext.menu.CheckItem} this
57417              * @param {Boolean} checked The checked value that was set
57418              * @method checkHandler
57419              */
57420             if(this.checkHandler){
57421                 this.on('checkchange', this.checkHandler, this.scope);
57422             }
57423             Ext.menu.MenuMgr.registerCheckable(this);
57424     },
57425
57426     // private
57427     onRender : function(c){
57428         Ext.menu.CheckItem.superclass.onRender.apply(this, arguments);
57429         if(this.group){
57430             this.el.addClass(this.groupClass);
57431         }
57432         if(this.checked){
57433             this.checked = false;
57434             this.setChecked(true, true);
57435         }
57436     },
57437
57438     // private
57439     destroy : function(){
57440         Ext.menu.MenuMgr.unregisterCheckable(this);
57441         Ext.menu.CheckItem.superclass.destroy.apply(this, arguments);
57442     },
57443
57444     /**
57445      * Set the checked state of this item
57446      * @param {Boolean} checked The new checked value
57447      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
57448      */
57449     setChecked : function(state, suppressEvent){
57450         var suppress = suppressEvent === true;
57451         if(this.checked != state && (suppress || this.fireEvent("beforecheckchange", this, state) !== false)){
57452             if(this.container){
57453                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
57454             }
57455             this.checked = state;
57456             if(!suppress){
57457                 this.fireEvent("checkchange", this, state);
57458             }
57459         }
57460     },
57461
57462     // private
57463     handleClick : function(e){
57464        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
57465            this.setChecked(!this.checked);
57466        }
57467        Ext.menu.CheckItem.superclass.handleClick.apply(this, arguments);
57468     }
57469 });
57470 Ext.reg('menucheckitem', Ext.menu.CheckItem);/**
57471  * @class Ext.menu.DateMenu
57472  * @extends Ext.menu.Menu
57473  * <p>A menu containing an {@link Ext.DatePicker} Component.</p>
57474  * <p>Notes:</p><div class="mdetail-params"><ul>
57475  * <li>Although not listed here, the <b>constructor</b> for this class
57476  * accepts all of the configuration options of <b>{@link Ext.DatePicker}</b>.</li>
57477  * <li>If subclassing DateMenu, any configuration options for the DatePicker must be
57478  * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.
57479  * Applying {@link Ext.DatePicker DatePicker} configuration settings to
57480  * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>
57481  * </ul></div>
57482  * @xtype datemenu
57483  */
57484  Ext.menu.DateMenu = Ext.extend(Ext.menu.Menu, {
57485     /** 
57486      * @cfg {Boolean} enableScrolling
57487      * @hide 
57488      */
57489     enableScrolling : false,
57490     /**
57491      * @cfg {Function} handler
57492      * Optional. A function that will handle the select event of this menu.
57493      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
57494      * <li><code>picker</code> : DatePicker<div class="sub-desc">The Ext.DatePicker.</div></li>
57495      * <li><code>date</code> : Date<div class="sub-desc">The selected date.</div></li>
57496      * </ul></div>
57497      */
57498     /**
57499      * @cfg {Object} scope
57500      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
57501      * function will be called.  Defaults to this DateMenu instance.
57502      */    
57503     /** 
57504      * @cfg {Boolean} hideOnClick
57505      * False to continue showing the menu after a date is selected, defaults to true.
57506      */
57507     hideOnClick : true,
57508     
57509     /** 
57510      * @cfg {String} pickerId
57511      * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.
57512      */
57513     pickerId : null,
57514     
57515     /** 
57516      * @cfg {Number} maxHeight
57517      * @hide 
57518      */
57519     /** 
57520      * @cfg {Number} scrollIncrement
57521      * @hide 
57522      */
57523     /**
57524      * The {@link Ext.DatePicker} instance for this DateMenu
57525      * @property picker
57526      * @type DatePicker
57527      */
57528     cls : 'x-date-menu',
57529     
57530     /**
57531      * @event click
57532      * @hide
57533      */
57534     
57535     /**
57536      * @event itemclick
57537      * @hide
57538      */
57539
57540     initComponent : function(){
57541         this.on('beforeshow', this.onBeforeShow, this);
57542         if(this.strict = (Ext.isIE7 && Ext.isStrict)){
57543             this.on('show', this.onShow, this, {single: true, delay: 20});
57544         }
57545         Ext.apply(this, {
57546             plain: true,
57547             showSeparator: false,
57548             items: this.picker = new Ext.DatePicker(Ext.applyIf({
57549                 internalRender: this.strict || !Ext.isIE,
57550                 ctCls: 'x-menu-date-item',
57551                 id: this.pickerId
57552             }, this.initialConfig))
57553         });
57554         this.picker.purgeListeners();
57555         Ext.menu.DateMenu.superclass.initComponent.call(this);
57556         /**
57557          * @event select
57558          * Fires when a date is selected from the {@link #picker Ext.DatePicker}
57559          * @param {DatePicker} picker The {@link #picker Ext.DatePicker}
57560          * @param {Date} date The selected date
57561          */
57562         this.relayEvents(this.picker, ['select']);
57563         this.on('show', this.picker.focus, this.picker);
57564         this.on('select', this.menuHide, this);
57565         if(this.handler){
57566             this.on('select', this.handler, this.scope || this);
57567         }
57568     },
57569
57570     menuHide : function() {
57571         if(this.hideOnClick){
57572             this.hide(true);
57573         }
57574     },
57575
57576     onBeforeShow : function(){
57577         if(this.picker){
57578             this.picker.hideMonthPicker(true);
57579         }
57580     },
57581
57582     onShow : function(){
57583         var el = this.picker.getEl();
57584         el.setWidth(el.getWidth()); //nasty hack for IE7 strict mode
57585     }
57586  });
57587  Ext.reg('datemenu', Ext.menu.DateMenu);
57588  /**
57589  * @class Ext.menu.ColorMenu
57590  * @extends Ext.menu.Menu
57591  * <p>A menu containing a {@link Ext.ColorPalette} Component.</p>
57592  * <p>Notes:</p><div class="mdetail-params"><ul>
57593  * <li>Although not listed here, the <b>constructor</b> for this class
57594  * accepts all of the configuration options of <b>{@link Ext.ColorPalette}</b>.</li>
57595  * <li>If subclassing ColorMenu, any configuration options for the ColorPalette must be
57596  * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.
57597  * Applying {@link Ext.ColorPalette ColorPalette} configuration settings to
57598  * <b><tt>this</tt></b> will <b>not</b> affect the ColorPalette's configuration.</li>
57599  * </ul></div> * 
57600  * @xtype colormenu
57601  */
57602  Ext.menu.ColorMenu = Ext.extend(Ext.menu.Menu, {
57603     /** 
57604      * @cfg {Boolean} enableScrolling
57605      * @hide 
57606      */
57607     enableScrolling : false,
57608     /**
57609      * @cfg {Function} handler
57610      * Optional. A function that will handle the select event of this menu.
57611      * The handler is passed the following parameters:<div class="mdetail-params"><ul>
57612      * <li><code>palette</code> : ColorPalette<div class="sub-desc">The {@link #palette Ext.ColorPalette}.</div></li>
57613      * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
57614      * </ul></div>
57615      */
57616     /**
57617      * @cfg {Object} scope
57618      * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
57619      * function will be called.  Defaults to this ColorMenu instance.
57620      */    
57621     
57622     /** 
57623      * @cfg {Boolean} hideOnClick
57624      * False to continue showing the menu after a color is selected, defaults to true.
57625      */
57626     hideOnClick : true,
57627     
57628     cls : 'x-color-menu',
57629     
57630     /** 
57631      * @cfg {String} paletteId
57632      * An id to assign to the underlying color palette. Defaults to <tt>null</tt>.
57633      */
57634     paletteId : null,
57635     
57636     /** 
57637      * @cfg {Number} maxHeight
57638      * @hide 
57639      */
57640     /** 
57641      * @cfg {Number} scrollIncrement
57642      * @hide 
57643      */
57644     /**
57645      * @property palette
57646      * @type ColorPalette
57647      * The {@link Ext.ColorPalette} instance for this ColorMenu
57648      */
57649     
57650     
57651     /**
57652      * @event click
57653      * @hide
57654      */
57655     
57656     /**
57657      * @event itemclick
57658      * @hide
57659      */
57660     
57661     initComponent : function(){
57662         Ext.apply(this, {
57663             plain: true,
57664             showSeparator: false,
57665             items: this.palette = new Ext.ColorPalette(Ext.applyIf({
57666                 id: this.paletteId
57667             }, this.initialConfig))
57668         });
57669         this.palette.purgeListeners();
57670         Ext.menu.ColorMenu.superclass.initComponent.call(this);
57671         /**
57672          * @event select
57673          * Fires when a color is selected from the {@link #palette Ext.ColorPalette}
57674          * @param {Ext.ColorPalette} palette The {@link #palette Ext.ColorPalette}
57675              * @param {String} color The 6-digit color hex code (without the # symbol)
57676          */
57677         this.relayEvents(this.palette, ['select']);
57678         this.on('select', this.menuHide, this);
57679         if(this.handler){
57680             this.on('select', this.handler, this.scope || this);
57681         }
57682     },
57683
57684     menuHide : function(){
57685         if(this.hideOnClick){
57686             this.hide(true);
57687         }
57688     }
57689 });
57690 Ext.reg('colormenu', Ext.menu.ColorMenu);
57691 /**
57692  * @class Ext.form.Field
57693  * @extends Ext.BoxComponent
57694  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
57695  * @constructor
57696  * Creates a new Field
57697  * @param {Object} config Configuration options
57698  * @xtype field
57699  */
57700 Ext.form.Field = Ext.extend(Ext.BoxComponent,  {
57701     /**
57702      * <p>The label Element associated with this Field. <b>Only available after this Field has been rendered by a
57703      * {@link form Ext.layout.FormLayout} layout manager.</b></p>
57704      * @type Ext.Element
57705      * @property label
57706      */
57707     /**
57708      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
57709      * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
57710      * no separate Ext components for those. Note that if you use <tt>inputType:'file'</tt>, {@link #emptyText}
57711      * is not supported and should be avoided.
57712      */
57713     /**
57714      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
57715      * not those which are built via applyTo (defaults to undefined).
57716      */
57717     /**
57718      * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
57719      */
57720     /**
57721      * @cfg {String} name The field's HTML name attribute (defaults to '').
57722      * <b>Note</b>: this property must be set if this field is to be automatically included with
57723      * {@link Ext.form.BasicForm#submit form submit()}.
57724      */
57725     /**
57726      * @cfg {String} cls A custom CSS class to apply to the field's underlying element (defaults to '').
57727      */
57728
57729     /**
57730      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to 'x-form-invalid')
57731      */
57732     invalidClass : 'x-form-invalid',
57733     /**
57734      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
57735      * (defaults to 'The value in this field is invalid')
57736      */
57737     invalidText : 'The value in this field is invalid',
57738     /**
57739      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to 'x-form-focus')
57740      */
57741     focusClass : 'x-form-focus',
57742     /**
57743      * @cfg {Boolean} preventMark
57744      * <tt>true</tt> to disable {@link #markInvalid marking the field invalid}.
57745      * Defaults to <tt>false</tt>.
57746      */
57747     /**
57748      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
57749       automatic validation (defaults to 'keyup').
57750      */
57751     validationEvent : 'keyup',
57752     /**
57753      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
57754      */
57755     validateOnBlur : true,
57756     /**
57757      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation
57758      * is initiated (defaults to 250)
57759      */
57760     validationDelay : 250,
57761     /**
57762      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
57763      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
57764      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
57765      * <pre><code>{tag: 'input', type: 'text', size: '20', autocomplete: 'off'}</code></pre>
57766      */
57767     defaultAutoCreate : {tag: 'input', type: 'text', size: '20', autocomplete: 'off'},
57768     /**
57769      * @cfg {String} fieldClass The default CSS class for the field (defaults to 'x-form-field')
57770      */
57771     fieldClass : 'x-form-field',
57772     /**
57773      * @cfg {String} msgTarget <p>The location where the message text set through {@link #markInvalid} should display.
57774      * Must be one of the following values:</p>
57775      * <div class="mdetail-params"><ul>
57776      * <li><code>qtip</code> Display a quick tip containing the message when the user hovers over the field. This is the default.
57777      * <div class="subdesc"><b>{@link Ext.QuickTips#init Ext.QuickTips.init} must have been called for this setting to work.</b></div</li>
57778      * <li><code>title</code> Display the message in a default browser title attribute popup.</li>
57779      * <li><code>under</code> Add a block div beneath the field containing the error message.</li>
57780      * <li><code>side</code> Add an error icon to the right of the field, displaying the message in a popup on hover.</li>
57781      * <li><code>[element id]</code> Add the error message directly to the innerHTML of the specified element.</li>
57782      * </ul></div>
57783      */
57784     msgTarget : 'qtip',
57785     /**
57786      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field
57787      * (defaults to 'normal').
57788      */
57789     msgFx : 'normal',
57790     /**
57791      * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
57792      * (defaults to <tt>false</tt>).
57793      * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
57794      * Setting <code>readOnly=true</code>, for example, will not disable triggering a
57795      * ComboBox or DateField; it gives you the option of forcing the user to choose
57796      * via the trigger without typing in the text box. To hide the trigger use
57797      * <code>{@link Ext.form.TriggerField#hideTrigger hideTrigger}</code>.</p>
57798      */
57799     readOnly : false,
57800     /**
57801      * @cfg {Boolean} disabled True to disable the field (defaults to false).
57802      * <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>,
57803      * disabled Fields will not be {@link Ext.form.BasicForm#submit submitted}.</p>
57804      */
57805     disabled : false,
57806     /**
57807      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
57808      * Defaults to <tt>true</tt>.
57809      */
57810     submitValue: true,
57811
57812     // private
57813     isFormField : true,
57814
57815     // private
57816     msgDisplay: '',
57817
57818     // private
57819     hasFocus : false,
57820
57821     // private
57822     initComponent : function(){
57823         Ext.form.Field.superclass.initComponent.call(this);
57824         this.addEvents(
57825             /**
57826              * @event focus
57827              * Fires when this field receives input focus.
57828              * @param {Ext.form.Field} this
57829              */
57830             'focus',
57831             /**
57832              * @event blur
57833              * Fires when this field loses input focus.
57834              * @param {Ext.form.Field} this
57835              */
57836             'blur',
57837             /**
57838              * @event specialkey
57839              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
57840              * To handle other keys see {@link Ext.Panel#keys} or {@link Ext.KeyMap}.
57841              * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
57842              * For example: <pre><code>
57843 var form = new Ext.form.FormPanel({
57844     ...
57845     items: [{
57846             fieldLabel: 'Field 1',
57847             name: 'field1',
57848             allowBlank: false
57849         },{
57850             fieldLabel: 'Field 2',
57851             name: 'field2',
57852             listeners: {
57853                 specialkey: function(field, e){
57854                     // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
57855                     // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
57856                     if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
57857                         var form = field.ownerCt.getForm();
57858                         form.submit();
57859                     }
57860                 }
57861             }
57862         }
57863     ],
57864     ...
57865 });
57866              * </code></pre>
57867              * @param {Ext.form.Field} this
57868              * @param {Ext.EventObject} e The event object
57869              */
57870             'specialkey',
57871             /**
57872              * @event change
57873              * Fires just before the field blurs if the field value has changed.
57874              * @param {Ext.form.Field} this
57875              * @param {Mixed} newValue The new value
57876              * @param {Mixed} oldValue The original value
57877              */
57878             'change',
57879             /**
57880              * @event invalid
57881              * Fires after the field has been marked as invalid.
57882              * @param {Ext.form.Field} this
57883              * @param {String} msg The validation message
57884              */
57885             'invalid',
57886             /**
57887              * @event valid
57888              * Fires after the field has been validated with no errors.
57889              * @param {Ext.form.Field} this
57890              */
57891             'valid'
57892         );
57893     },
57894
57895     /**
57896      * Returns the {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
57897      * attribute of the field if available.
57898      * @return {String} name The field {@link Ext.form.Field#name name} or {@link Ext.form.ComboBox#hiddenName hiddenName}
57899      */
57900     getName : function(){
57901         return this.rendered && this.el.dom.name ? this.el.dom.name : this.name || this.id || '';
57902     },
57903
57904     // private
57905     onRender : function(ct, position){
57906         if(!this.el){
57907             var cfg = this.getAutoCreate();
57908
57909             if(!cfg.name){
57910                 cfg.name = this.name || this.id;
57911             }
57912             if(this.inputType){
57913                 cfg.type = this.inputType;
57914             }
57915             this.autoEl = cfg;
57916         }
57917         Ext.form.Field.superclass.onRender.call(this, ct, position);
57918         if(this.submitValue === false){
57919             this.el.dom.removeAttribute('name');
57920         }
57921         var type = this.el.dom.type;
57922         if(type){
57923             if(type == 'password'){
57924                 type = 'text';
57925             }
57926             this.el.addClass('x-form-'+type);
57927         }
57928         if(this.readOnly){
57929             this.setReadOnly(true);
57930         }
57931         if(this.tabIndex !== undefined){
57932             this.el.dom.setAttribute('tabIndex', this.tabIndex);
57933         }
57934
57935         this.el.addClass([this.fieldClass, this.cls]);
57936     },
57937
57938     // private
57939     getItemCt : function(){
57940         return this.itemCt;
57941     },
57942
57943     // private
57944     initValue : function(){
57945         if(this.value !== undefined){
57946             this.setValue(this.value);
57947         }else if(!Ext.isEmpty(this.el.dom.value) && this.el.dom.value != this.emptyText){
57948             this.setValue(this.el.dom.value);
57949         }
57950         /**
57951          * The original value of the field as configured in the {@link #value} configuration, or
57952          * as loaded by the last form load operation if the form's {@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
57953          * setting is <code>true</code>.
57954          * @type mixed
57955          * @property originalValue
57956          */
57957         this.originalValue = this.getValue();
57958     },
57959
57960     /**
57961      * <p>Returns true if the value of this Field has been changed from its original value.
57962      * Will return false if the field is disabled or has not been rendered yet.</p>
57963      * <p>Note that if the owning {@link Ext.form.BasicForm form} was configured with
57964      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
57965      * then the <i>original value</i> is updated when the values are loaded by
57966      * {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#setValues setValues}.</p>
57967      * @return {Boolean} True if this field has been changed from its original value (and
57968      * is not disabled), false otherwise.
57969      */
57970     isDirty : function() {
57971         if(this.disabled || !this.rendered) {
57972             return false;
57973         }
57974         return String(this.getValue()) !== String(this.originalValue);
57975     },
57976
57977     /**
57978      * Sets the read only state of this field.
57979      * @param {Boolean} readOnly Whether the field should be read only.
57980      */
57981     setReadOnly : function(readOnly){
57982         if(this.rendered){
57983             this.el.dom.readOnly = readOnly;
57984         }
57985         this.readOnly = readOnly;
57986     },
57987
57988     // private
57989     afterRender : function(){
57990         Ext.form.Field.superclass.afterRender.call(this);
57991         this.initEvents();
57992         this.initValue();
57993     },
57994
57995     // private
57996     fireKey : function(e){
57997         if(e.isSpecialKey()){
57998             this.fireEvent('specialkey', this, e);
57999         }
58000     },
58001
58002     /**
58003      * Resets the current field value to the originally loaded value and clears any validation messages.
58004      * See {@link Ext.form.BasicForm}.{@link Ext.form.BasicForm#trackResetOnLoad trackResetOnLoad}
58005      */
58006     reset : function(){
58007         this.setValue(this.originalValue);
58008         this.clearInvalid();
58009     },
58010
58011     // private
58012     initEvents : function(){
58013         this.mon(this.el, Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.fireKey,  this);
58014         this.mon(this.el, 'focus', this.onFocus, this);
58015
58016         // standardise buffer across all browsers + OS-es for consistent event order.
58017         // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
58018         this.mon(this.el, 'blur', this.onBlur, this, this.inEditor ? {buffer:10} : null);
58019     },
58020
58021     // private
58022     preFocus: Ext.emptyFn,
58023
58024     // private
58025     onFocus : function(){
58026         this.preFocus();
58027         if(this.focusClass){
58028             this.el.addClass(this.focusClass);
58029         }
58030         if(!this.hasFocus){
58031             this.hasFocus = true;
58032             /**
58033              * <p>The value that the Field had at the time it was last focused. This is the value that is passed
58034              * to the {@link #change} event which is fired if the value has been changed when the Field is blurred.</p>
58035              * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
58036              * @type mixed
58037              * @property startValue
58038              */
58039             this.startValue = this.getValue();
58040             this.fireEvent('focus', this);
58041         }
58042     },
58043
58044     // private
58045     beforeBlur : Ext.emptyFn,
58046
58047     // private
58048     onBlur : function(){
58049         this.beforeBlur();
58050         if(this.focusClass){
58051             this.el.removeClass(this.focusClass);
58052         }
58053         this.hasFocus = false;
58054         if(this.validationEvent !== false && (this.validateOnBlur || this.validationEvent == 'blur')){
58055             this.validate();
58056         }
58057         var v = this.getValue();
58058         if(String(v) !== String(this.startValue)){
58059             this.fireEvent('change', this, v, this.startValue);
58060         }
58061         this.fireEvent('blur', this);
58062         this.postBlur();
58063     },
58064
58065     // private
58066     postBlur : Ext.emptyFn,
58067
58068     /**
58069      * Returns whether or not the field value is currently valid by
58070      * {@link #validateValue validating} the {@link #processValue processed value}
58071      * of the field. <b>Note</b>: {@link #disabled} fields are ignored.
58072      * @param {Boolean} preventMark True to disable marking the field invalid
58073      * @return {Boolean} True if the value is valid, else false
58074      */
58075     isValid : function(preventMark){
58076         if(this.disabled){
58077             return true;
58078         }
58079         var restore = this.preventMark;
58080         this.preventMark = preventMark === true;
58081         var v = this.validateValue(this.processValue(this.getRawValue()));
58082         this.preventMark = restore;
58083         return v;
58084     },
58085
58086     /**
58087      * Validates the field value
58088      * @return {Boolean} True if the value is valid, else false
58089      */
58090     validate : function(){
58091         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
58092             this.clearInvalid();
58093             return true;
58094         }
58095         return false;
58096     },
58097
58098     /**
58099      * This method should only be overridden if necessary to prepare raw values
58100      * for validation (see {@link #validate} and {@link #isValid}).  This method
58101      * is expected to return the processed value for the field which will
58102      * be used for validation (see validateValue method).
58103      * @param {Mixed} value
58104      */
58105     processValue : function(value){
58106         return value;
58107     },
58108
58109     /**
58110      * Uses getErrors to build an array of validation errors. If any errors are found, markInvalid is called
58111      * with the first and false is returned, otherwise true is returned. Previously, subclasses were invited
58112      * to provide an implementation of this to process validations - from 3.2 onwards getErrors should be
58113      * overridden instead.
58114      * @param {Mixed} The current value of the field
58115      * @return {Boolean} True if all validations passed, false if one or more failed
58116      */
58117      validateValue : function(value) {
58118          //currently, we only show 1 error at a time for a field, so just use the first one
58119          var error = this.getErrors(value)[0];
58120
58121          if (error == undefined) {
58122              return true;
58123          } else {
58124              this.markInvalid(error);
58125              return false;
58126          }
58127      },
58128     
58129     /**
58130      * Runs this field's validators and returns an array of error messages for any validation failures.
58131      * This is called internally during validation and would not usually need to be used manually.
58132      * Each subclass should override or augment the return value to provide their own errors
58133      * @return {Array} All error messages for this field
58134      */
58135     getErrors: function() {
58136         return [];
58137     },
58138
58139     /**
58140      * Gets the active error message for this field.
58141      * @return {String} Returns the active error message on the field, if there is no error, an empty string is returned.
58142      */
58143     getActiveError : function(){
58144         return this.activeError || '';
58145     },
58146
58147     /**
58148      * <p>Display an error message associated with this field, using {@link #msgTarget} to determine how to
58149      * display the message and applying {@link #invalidClass} to the field's UI element.</p>
58150      * <p><b>Note</b>: this method does not cause the Field's {@link #validate} method to return <code>false</code>
58151      * if the value does <i>pass</i> validation. So simply marking a Field as invalid will not prevent
58152      * submission of forms submitted with the {@link Ext.form.Action.Submit#clientValidation} option set.</p>
58153      * {@link #isValid invalid}.
58154      * @param {String} msg (optional) The validation message (defaults to {@link #invalidText})
58155      */
58156     markInvalid : function(msg){
58157         //don't set the error icon if we're not rendered or marking is prevented
58158         if (this.rendered && !this.preventMark) {
58159             msg = msg || this.invalidText;
58160
58161             var mt = this.getMessageHandler();
58162             if(mt){
58163                 mt.mark(this, msg);
58164             }else if(this.msgTarget){
58165                 this.el.addClass(this.invalidClass);
58166                 var t = Ext.getDom(this.msgTarget);
58167                 if(t){
58168                     t.innerHTML = msg;
58169                     t.style.display = this.msgDisplay;
58170                 }
58171             }
58172         }
58173         
58174         this.setActiveError(msg);
58175     },
58176     
58177     /**
58178      * Clear any invalid styles/messages for this field
58179      */
58180     clearInvalid : function(){
58181         //don't remove the error icon if we're not rendered or marking is prevented
58182         if (this.rendered && !this.preventMark) {
58183             this.el.removeClass(this.invalidClass);
58184             var mt = this.getMessageHandler();
58185             if(mt){
58186                 mt.clear(this);
58187             }else if(this.msgTarget){
58188                 this.el.removeClass(this.invalidClass);
58189                 var t = Ext.getDom(this.msgTarget);
58190                 if(t){
58191                     t.innerHTML = '';
58192                     t.style.display = 'none';
58193                 }
58194             }
58195         }
58196         
58197         this.unsetActiveError();
58198     },
58199
58200     /**
58201      * Sets the current activeError to the given string. Fires the 'invalid' event.
58202      * This does not set up the error icon, only sets the message and fires the event. To show the error icon,
58203      * use markInvalid instead, which calls this method internally
58204      * @param {String} msg The error message
58205      * @param {Boolean} suppressEvent True to suppress the 'invalid' event from being fired
58206      */
58207     setActiveError: function(msg, suppressEvent) {
58208         this.activeError = msg;
58209         if (suppressEvent !== true) this.fireEvent('invalid', this, msg);
58210     },
58211     
58212     /**
58213      * Clears the activeError and fires the 'valid' event. This is called internally by clearInvalid and would not
58214      * usually need to be called manually
58215      * @param {Boolean} suppressEvent True to suppress the 'invalid' event from being fired
58216      */
58217     unsetActiveError: function(suppressEvent) {
58218         delete this.activeError;
58219         if (suppressEvent !== true) this.fireEvent('valid', this);
58220     },
58221
58222     // private
58223     getMessageHandler : function(){
58224         return Ext.form.MessageTargets[this.msgTarget];
58225     },
58226
58227     // private
58228     getErrorCt : function(){
58229         return this.el.findParent('.x-form-element', 5, true) || // use form element wrap if available
58230             this.el.findParent('.x-form-field-wrap', 5, true);   // else direct field wrap
58231     },
58232
58233     // Alignment for 'under' target
58234     alignErrorEl : function(){
58235         this.errorEl.setWidth(this.getErrorCt().getWidth(true) - 20);
58236     },
58237
58238     // Alignment for 'side' target
58239     alignErrorIcon : function(){
58240         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
58241     },
58242
58243     /**
58244      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
58245      * @return {Mixed} value The field value
58246      */
58247     getRawValue : function(){
58248         var v = this.rendered ? this.el.getValue() : Ext.value(this.value, '');
58249         if(v === this.emptyText){
58250             v = '';
58251         }
58252         return v;
58253     },
58254
58255     /**
58256      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
58257      * @return {Mixed} value The field value
58258      */
58259     getValue : function(){
58260         if(!this.rendered) {
58261             return this.value;
58262         }
58263         var v = this.el.getValue();
58264         if(v === this.emptyText || v === undefined){
58265             v = '';
58266         }
58267         return v;
58268     },
58269
58270     /**
58271      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
58272      * @param {Mixed} value The value to set
58273      * @return {Mixed} value The field value that is set
58274      */
58275     setRawValue : function(v){
58276         return this.rendered ? (this.el.dom.value = (Ext.isEmpty(v) ? '' : v)) : '';
58277     },
58278
58279     /**
58280      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
58281      * @param {Mixed} value The value to set
58282      * @return {Ext.form.Field} this
58283      */
58284     setValue : function(v){
58285         this.value = v;
58286         if(this.rendered){
58287             this.el.dom.value = (Ext.isEmpty(v) ? '' : v);
58288             this.validate();
58289         }
58290         return this;
58291     },
58292
58293     // private, does not work for all fields
58294     append : function(v){
58295          this.setValue([this.getValue(), v].join(''));
58296     }
58297
58298     /**
58299      * @cfg {Boolean} autoWidth @hide
58300      */
58301     /**
58302      * @cfg {Boolean} autoHeight @hide
58303      */
58304
58305     /**
58306      * @cfg {String} autoEl @hide
58307      */
58308 });
58309
58310
58311 Ext.form.MessageTargets = {
58312     'qtip' : {
58313         mark: function(field, msg){
58314             field.el.addClass(field.invalidClass);
58315             field.el.dom.qtip = msg;
58316             field.el.dom.qclass = 'x-form-invalid-tip';
58317             if(Ext.QuickTips){ // fix for floating editors interacting with DND
58318                 Ext.QuickTips.enable();
58319             }
58320         },
58321         clear: function(field){
58322             field.el.removeClass(field.invalidClass);
58323             field.el.dom.qtip = '';
58324         }
58325     },
58326     'title' : {
58327         mark: function(field, msg){
58328             field.el.addClass(field.invalidClass);
58329             field.el.dom.title = msg;
58330         },
58331         clear: function(field){
58332             field.el.dom.title = '';
58333         }
58334     },
58335     'under' : {
58336         mark: function(field, msg){
58337             field.el.addClass(field.invalidClass);
58338             if(!field.errorEl){
58339                 var elp = field.getErrorCt();
58340                 if(!elp){ // field has no container el
58341                     field.el.dom.title = msg;
58342                     return;
58343                 }
58344                 field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
58345                 field.on('resize', field.alignErrorEl, field);
58346                 field.on('destroy', function(){
58347                     Ext.destroy(this.errorEl);
58348                 }, field);
58349             }
58350             field.alignErrorEl();
58351             field.errorEl.update(msg);
58352             Ext.form.Field.msgFx[field.msgFx].show(field.errorEl, field);
58353         },
58354         clear: function(field){
58355             field.el.removeClass(field.invalidClass);
58356             if(field.errorEl){
58357                 Ext.form.Field.msgFx[field.msgFx].hide(field.errorEl, field);
58358             }else{
58359                 field.el.dom.title = '';
58360             }
58361         }
58362     },
58363     'side' : {
58364         mark: function(field, msg){
58365             field.el.addClass(field.invalidClass);
58366             if(!field.errorIcon){
58367                 var elp = field.getErrorCt();
58368                 // field has no container el
58369                 if(!elp){
58370                     field.el.dom.title = msg;
58371                     return;
58372                 }
58373                 field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
58374                 if (field.ownerCt) {
58375                     field.ownerCt.on('afterlayout', field.alignErrorIcon, field);
58376                     field.ownerCt.on('expand', field.alignErrorIcon, field);
58377                 }
58378                 field.on('resize', field.alignErrorIcon, field);
58379                 field.on('destroy', function(){
58380                     Ext.destroy(this.errorIcon);
58381                 }, field);
58382             }
58383             field.alignErrorIcon();
58384             field.errorIcon.dom.qtip = msg;
58385             field.errorIcon.dom.qclass = 'x-form-invalid-tip';
58386             field.errorIcon.show();
58387         },
58388         clear: function(field){
58389             field.el.removeClass(field.invalidClass);
58390             if(field.errorIcon){
58391                 field.errorIcon.dom.qtip = '';
58392                 field.errorIcon.hide();
58393             }else{
58394                 field.el.dom.title = '';
58395             }
58396         }
58397     }
58398 };
58399
58400 // anything other than normal should be considered experimental
58401 Ext.form.Field.msgFx = {
58402     normal : {
58403         show: function(msgEl, f){
58404             msgEl.setDisplayed('block');
58405         },
58406
58407         hide : function(msgEl, f){
58408             msgEl.setDisplayed(false).update('');
58409         }
58410     },
58411
58412     slide : {
58413         show: function(msgEl, f){
58414             msgEl.slideIn('t', {stopFx:true});
58415         },
58416
58417         hide : function(msgEl, f){
58418             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
58419         }
58420     },
58421
58422     slideRight : {
58423         show: function(msgEl, f){
58424             msgEl.fixDisplay();
58425             msgEl.alignTo(f.el, 'tl-tr');
58426             msgEl.slideIn('l', {stopFx:true});
58427         },
58428
58429         hide : function(msgEl, f){
58430             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
58431         }
58432     }
58433 };
58434 Ext.reg('field', Ext.form.Field);
58435 /**
58436  * @class Ext.form.TextField
58437  * @extends Ext.form.Field
58438  * <p>Basic text field.  Can be used as a direct replacement for traditional text inputs,
58439  * or as the base class for more sophisticated input controls (like {@link Ext.form.TextArea}
58440  * and {@link Ext.form.ComboBox}).</p>
58441  * <p><b><u>Validation</u></b></p>
58442  * <p>The validation procedure is described in the documentation for {@link #validateValue}.</p>
58443  * <p><b><u>Alter Validation Behavior</u></b></p>
58444  * <p>Validation behavior for each field can be configured:</p>
58445  * <div class="mdetail-params"><ul>
58446  * <li><code>{@link Ext.form.TextField#invalidText invalidText}</code> : the default validation message to
58447  * show if any validation step above does not provide a message when invalid</li>
58448  * <li><code>{@link Ext.form.TextField#maskRe maskRe}</code> : filter out keystrokes before any validation occurs</li>
58449  * <li><code>{@link Ext.form.TextField#stripCharsRe stripCharsRe}</code> : filter characters after being typed in,
58450  * but before being validated</li>
58451  * <li><code>{@link Ext.form.Field#invalidClass invalidClass}</code> : alternate style when invalid</li>
58452  * <li><code>{@link Ext.form.Field#validateOnBlur validateOnBlur}</code>,
58453  * <code>{@link Ext.form.Field#validationDelay validationDelay}</code>, and
58454  * <code>{@link Ext.form.Field#validationEvent validationEvent}</code> : modify how/when validation is triggered</li>
58455  * </ul></div>
58456  * 
58457  * @constructor Creates a new TextField
58458  * @param {Object} config Configuration options
58459  * 
58460  * @xtype textfield
58461  */
58462 Ext.form.TextField = Ext.extend(Ext.form.Field,  {
58463     /**
58464      * @cfg {String} vtypeText A custom error message to display in place of the default message provided
58465      * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>''</tt>).  <b>Note</b>:
58466      * only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
58467      */
58468     /**
58469      * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
58470      * before validation (defaults to <tt>null</tt>).
58471      */
58472     /**
58473      * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
58474      * (defaults to <tt>false</tt>)
58475      */
58476     grow : false,
58477     /**
58478      * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
58479      * to <tt>30</tt>)
58480      */
58481     growMin : 30,
58482     /**
58483      * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
58484      * to <tt>800</tt>)
58485      */
58486     growMax : 800,
58487     /**
58488      * @cfg {String} vtype A validation type name as defined in {@link Ext.form.VTypes} (defaults to <tt>null</tt>)
58489      */
58490     vtype : null,
58491     /**
58492      * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
58493      * not match (defaults to <tt>null</tt>)
58494      */
58495     maskRe : null,
58496     /**
58497      * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
58498      * to <tt>false</tt>)
58499      */
58500     disableKeyFilter : false,
58501     /**
58502      * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
58503      * <tt>true</tt>)
58504      */
58505     allowBlank : true,
58506     /**
58507      * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
58508      */
58509     minLength : 0,
58510     /**
58511      * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
58512      * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
58513      * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
58514      * entered into the field use <tt><b>{@link Ext.form.Field#autoCreate autoCreate}</b></tt> to add
58515      * any attributes you want to a field, for example:<pre><code>
58516 var myField = new Ext.form.NumberField({
58517     id: 'mobile',
58518     anchor:'90%',
58519     fieldLabel: 'Mobile',
58520     maxLength: 16, // for validation
58521     autoCreate: {tag: 'input', type: 'text', size: '20', autocomplete: 'off', maxlength: '10'}
58522 });
58523 </code></pre>
58524      */
58525     maxLength : Number.MAX_VALUE,
58526     /**
58527      * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
58528      * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
58529      */
58530     minLengthText : 'The minimum length for this field is {0}',
58531     /**
58532      * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
58533      * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
58534      */
58535     maxLengthText : 'The maximum length for this field is {0}',
58536     /**
58537      * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
58538      * receives input focus (defaults to <tt>false</tt>)
58539      */
58540     selectOnFocus : false,
58541     /**
58542      * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
58543      * fails (defaults to <tt>'This field is required'</tt>)
58544      */
58545     blankText : 'This field is required',
58546     /**
58547      * @cfg {Function} validator
58548      * <p>A custom validation function to be called during field validation ({@link #validateValue})
58549      * (defaults to <tt>null</tt>). If specified, this function will be called first, allowing the
58550      * developer to override the default validation process.</p>
58551      * <br><p>This function will be passed the following Parameters:</p>
58552      * <div class="mdetail-params"><ul>
58553      * <li><code>value</code>: <i>Mixed</i>
58554      * <div class="sub-desc">The current field value</div></li>
58555      * </ul></div>
58556      * <br><p>This function is to Return:</p>
58557      * <div class="mdetail-params"><ul>
58558      * <li><code>true</code>: <i>Boolean</i>
58559      * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
58560      * <li><code>msg</code>: <i>String</i>
58561      * <div class="sub-desc">An error message if the value is invalid</div></li>
58562      * </ul></div>
58563      */
58564     validator : null,
58565     /**
58566      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
58567      * (defaults to <tt>null</tt>). If the test fails, the field will be marked invalid using
58568      * <b><tt>{@link #regexText}</tt></b>.
58569      */
58570     regex : null,
58571     /**
58572      * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
58573      * test fails during validation (defaults to <tt>''</tt>)
58574      */
58575     regexText : '',
58576     /**
58577      * @cfg {String} emptyText The default text to place into an empty field (defaults to <tt>null</tt>).
58578      * <b>Note</b>: that this value will be submitted to the server if this field is enabled and configured
58579      * with a {@link #name}.
58580      */
58581     emptyText : null,
58582     /**
58583      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
58584      * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
58585      * depending on the current field value.
58586      */
58587     emptyClass : 'x-form-empty-field',
58588
58589     /**
58590      * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input
58591      * field (defaults to <tt>false</tt>)
58592      */
58593
58594     initComponent : function(){
58595         Ext.form.TextField.superclass.initComponent.call(this);
58596         this.addEvents(
58597             /**
58598              * @event autosize
58599              * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered. The field may or
58600              * may not have actually changed size according to the default logic, but this event provides
58601              * a hook for the developer to apply additional logic at runtime to resize the field if needed.
58602              * @param {Ext.form.Field} this This text field
58603              * @param {Number} width The new field width
58604              */
58605             'autosize',
58606
58607             /**
58608              * @event keydown
58609              * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
58610              * is set to true.
58611              * @param {Ext.form.TextField} this This text field
58612              * @param {Ext.EventObject} e
58613              */
58614             'keydown',
58615             /**
58616              * @event keyup
58617              * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
58618              * is set to true.
58619              * @param {Ext.form.TextField} this This text field
58620              * @param {Ext.EventObject} e
58621              */
58622             'keyup',
58623             /**
58624              * @event keypress
58625              * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
58626              * is set to true.
58627              * @param {Ext.form.TextField} this This text field
58628              * @param {Ext.EventObject} e
58629              */
58630             'keypress'
58631         );
58632     },
58633
58634     // private
58635     initEvents : function(){
58636         Ext.form.TextField.superclass.initEvents.call(this);
58637         if(this.validationEvent == 'keyup'){
58638             this.validationTask = new Ext.util.DelayedTask(this.validate, this);
58639             this.mon(this.el, 'keyup', this.filterValidation, this);
58640         }
58641         else if(this.validationEvent !== false && this.validationEvent != 'blur'){
58642                 this.mon(this.el, this.validationEvent, this.validate, this, {buffer: this.validationDelay});
58643         }
58644         if(this.selectOnFocus || this.emptyText){            
58645             this.mon(this.el, 'mousedown', this.onMouseDown, this);
58646             
58647             if(this.emptyText){
58648                 this.applyEmptyText();
58649             }
58650         }
58651         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Ext.form.VTypes[this.vtype+'Mask']))){
58652                 this.mon(this.el, 'keypress', this.filterKeys, this);
58653         }
58654         if(this.grow){
58655                 this.mon(this.el, 'keyup', this.onKeyUpBuffered, this, {buffer: 50});
58656                         this.mon(this.el, 'click', this.autoSize, this);
58657         }
58658         if(this.enableKeyEvents){
58659             this.mon(this.el, {
58660                 scope: this,
58661                 keyup: this.onKeyUp,
58662                 keydown: this.onKeyDown,
58663                 keypress: this.onKeyPress
58664             });
58665         }
58666     },
58667     
58668     onMouseDown: function(e){
58669         if(!this.hasFocus){
58670             this.mon(this.el, 'mouseup', Ext.emptyFn, this, { single: true, preventDefault: true });
58671         }
58672     },
58673
58674     processValue : function(value){
58675         if(this.stripCharsRe){
58676             var newValue = value.replace(this.stripCharsRe, '');
58677             if(newValue !== value){
58678                 this.setRawValue(newValue);
58679                 return newValue;
58680             }
58681         }
58682         return value;
58683     },
58684
58685     filterValidation : function(e){
58686         if(!e.isNavKeyPress()){
58687             this.validationTask.delay(this.validationDelay);
58688         }
58689     },
58690     
58691     //private
58692     onDisable: function(){
58693         Ext.form.TextField.superclass.onDisable.call(this);
58694         if(Ext.isIE){
58695             this.el.dom.unselectable = 'on';
58696         }
58697     },
58698     
58699     //private
58700     onEnable: function(){
58701         Ext.form.TextField.superclass.onEnable.call(this);
58702         if(Ext.isIE){
58703             this.el.dom.unselectable = '';
58704         }
58705     },
58706
58707     // private
58708     onKeyUpBuffered : function(e){
58709         if(this.doAutoSize(e)){
58710             this.autoSize();
58711         }
58712     },
58713     
58714     // private
58715     doAutoSize : function(e){
58716         return !e.isNavKeyPress();
58717     },
58718
58719     // private
58720     onKeyUp : function(e){
58721         this.fireEvent('keyup', this, e);
58722     },
58723
58724     // private
58725     onKeyDown : function(e){
58726         this.fireEvent('keydown', this, e);
58727     },
58728
58729     // private
58730     onKeyPress : function(e){
58731         this.fireEvent('keypress', this, e);
58732     },
58733
58734     /**
58735      * Resets the current field value to the originally-loaded value and clears any validation messages.
58736      * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyClass}</b></tt> if the
58737      * original value was blank.
58738      */
58739     reset : function(){
58740         Ext.form.TextField.superclass.reset.call(this);
58741         this.applyEmptyText();
58742     },
58743
58744     applyEmptyText : function(){
58745         if(this.rendered && this.emptyText && this.getRawValue().length < 1 && !this.hasFocus){
58746             this.setRawValue(this.emptyText);
58747             this.el.addClass(this.emptyClass);
58748         }
58749     },
58750
58751     // private
58752     preFocus : function(){
58753         var el = this.el;
58754         if(this.emptyText){
58755             if(el.dom.value == this.emptyText){
58756                 this.setRawValue('');
58757             }
58758             el.removeClass(this.emptyClass);
58759         }
58760         if(this.selectOnFocus){
58761             el.dom.select();
58762         }
58763     },
58764
58765     // private
58766     postBlur : function(){
58767         this.applyEmptyText();
58768     },
58769
58770     // private
58771     filterKeys : function(e){
58772         if(e.ctrlKey){
58773             return;
58774         }
58775         var k = e.getKey();
58776         if(Ext.isGecko && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
58777             return;
58778         }
58779         var cc = String.fromCharCode(e.getCharCode());
58780         if(!Ext.isGecko && e.isSpecialKey() && !cc){
58781             return;
58782         }
58783         if(!this.maskRe.test(cc)){
58784             e.stopEvent();
58785         }
58786     },
58787
58788     setValue : function(v){
58789         if(this.emptyText && this.el && !Ext.isEmpty(v)){
58790             this.el.removeClass(this.emptyClass);
58791         }
58792         Ext.form.TextField.superclass.setValue.apply(this, arguments);
58793         this.applyEmptyText();
58794         this.autoSize();
58795         return this;
58796     },
58797
58798     /**
58799      * <p>Validates a value according to the field's validation rules and returns an array of errors
58800      * for any failing validations. Validation rules are processed in the following order:</p>
58801      * <div class="mdetail-params"><ul>
58802      * 
58803      * <li><b>1. Field specific validator</b>
58804      * <div class="sub-desc">
58805      * <p>A validator offers a way to customize and reuse a validation specification.
58806      * If a field is configured with a <code>{@link #validator}</code>
58807      * function, it will be passed the current field value.  The <code>{@link #validator}</code>
58808      * function is expected to return either:
58809      * <div class="mdetail-params"><ul>
58810      * <li>Boolean <tt>true</tt> if the value is valid (validation continues).</li>
58811      * <li>a String to represent the invalid message if invalid (validation halts).</li>
58812      * </ul></div>
58813      * </div></li>
58814      * 
58815      * <li><b>2. Basic Validation</b>
58816      * <div class="sub-desc">
58817      * <p>If the <code>{@link #validator}</code> has not halted validation,
58818      * basic validation proceeds as follows:</p>
58819      * 
58820      * <div class="mdetail-params"><ul>
58821      * 
58822      * <li><code>{@link #allowBlank}</code> : (Invalid message =
58823      * <code>{@link #emptyText}</code>)<div class="sub-desc">
58824      * Depending on the configuration of <code>{@link #allowBlank}</code>, a
58825      * blank field will cause validation to halt at this step and return
58826      * Boolean true or false accordingly.  
58827      * </div></li>
58828      * 
58829      * <li><code>{@link #minLength}</code> : (Invalid message =
58830      * <code>{@link #minLengthText}</code>)<div class="sub-desc">
58831      * If the passed value does not satisfy the <code>{@link #minLength}</code>
58832      * specified, validation halts.
58833      * </div></li>
58834      * 
58835      * <li><code>{@link #maxLength}</code> : (Invalid message =
58836      * <code>{@link #maxLengthText}</code>)<div class="sub-desc">
58837      * If the passed value does not satisfy the <code>{@link #maxLength}</code>
58838      * specified, validation halts.
58839      * </div></li>
58840      * 
58841      * </ul></div>
58842      * </div></li>
58843      * 
58844      * <li><b>3. Preconfigured Validation Types (VTypes)</b>
58845      * <div class="sub-desc">
58846      * <p>If none of the prior validation steps halts validation, a field
58847      * configured with a <code>{@link #vtype}</code> will utilize the
58848      * corresponding {@link Ext.form.VTypes VTypes} validation function.
58849      * If invalid, either the field's <code>{@link #vtypeText}</code> or
58850      * the VTypes vtype Text property will be used for the invalid message.
58851      * Keystrokes on the field will be filtered according to the VTypes
58852      * vtype Mask property.</p>
58853      * </div></li>
58854      * 
58855      * <li><b>4. Field specific regex test</b>
58856      * <div class="sub-desc">
58857      * <p>If none of the prior validation steps halts validation, a field's
58858      * configured <code>{@link #regex}</code> test will be processed.
58859      * The invalid message for this test is configured with
58860      * <code>{@link #regexText}</code>.</p>
58861      * </div></li>
58862      * 
58863      * @param {Mixed} value The value to validate. The processed raw value will be used if nothing is passed
58864      * @return {Array} Array of any validation errors
58865      */
58866     getErrors: function(value) {
58867         var errors = Ext.form.TextField.superclass.getErrors.apply(this, arguments);
58868         
58869         value = value || this.processValue(this.getRawValue());        
58870         
58871         if (Ext.isFunction(this.validator)) {
58872             var msg = this.validator(value);
58873             if (msg !== true) {
58874                 errors.push(msg);
58875             }
58876         }
58877         
58878         if (value.length < 1 || value === this.emptyText) {
58879             if (this.allowBlank) {
58880                 //if value is blank and allowBlank is true, there cannot be any additional errors
58881                 return errors;
58882             } else {
58883                 errors.push(this.blankText);
58884             }
58885         }
58886         
58887         if (!this.allowBlank && (value.length < 1 || value === this.emptyText)) { // if it's blank
58888             errors.push(this.blankText);
58889         }
58890         
58891         if (value.length < this.minLength) {
58892             errors.push(String.format(this.minLengthText, this.minLength));
58893         }
58894         
58895         if (value.length > this.maxLength) {
58896             errors.push(String.format(this.maxLengthText, this.maxLength));
58897         }
58898         
58899         if (this.vtype) {
58900             var vt = Ext.form.VTypes;
58901             if(!vt[this.vtype](value, this)){
58902                 errors.push(this.vtypeText || vt[this.vtype +'Text']);
58903             }
58904         }
58905         
58906         if (this.regex && !this.regex.test(value)) {
58907             errors.push(this.regexText);
58908         }
58909         
58910         return errors;
58911     },
58912
58913     /**
58914      * Selects text in this field
58915      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
58916      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
58917      */
58918     selectText : function(start, end){
58919         var v = this.getRawValue();
58920         var doFocus = false;
58921         if(v.length > 0){
58922             start = start === undefined ? 0 : start;
58923             end = end === undefined ? v.length : end;
58924             var d = this.el.dom;
58925             if(d.setSelectionRange){
58926                 d.setSelectionRange(start, end);
58927             }else if(d.createTextRange){
58928                 var range = d.createTextRange();
58929                 range.moveStart('character', start);
58930                 range.moveEnd('character', end-v.length);
58931                 range.select();
58932             }
58933             doFocus = Ext.isGecko || Ext.isOpera;
58934         }else{
58935             doFocus = true;
58936         }
58937         if(doFocus){
58938             this.focus();
58939         }
58940     },
58941
58942     /**
58943      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
58944      * This only takes effect if <tt><b>{@link #grow}</b> = true</tt>, and fires the {@link #autosize} event.
58945      */
58946     autoSize : function(){
58947         if(!this.grow || !this.rendered){
58948             return;
58949         }
58950         if(!this.metrics){
58951             this.metrics = Ext.util.TextMetrics.createInstance(this.el);
58952         }
58953         var el = this.el;
58954         var v = el.dom.value;
58955         var d = document.createElement('div');
58956         d.appendChild(document.createTextNode(v));
58957         v = d.innerHTML;
58958         Ext.removeNode(d);
58959         d = null;
58960         v += '&#160;';
58961         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
58962         this.el.setWidth(w);
58963         this.fireEvent('autosize', this, w);
58964     },
58965         
58966         onDestroy: function(){
58967                 if(this.validationTask){
58968                         this.validationTask.cancel();
58969                         this.validationTask = null;
58970                 }
58971                 Ext.form.TextField.superclass.onDestroy.call(this);
58972         }
58973 });
58974 Ext.reg('textfield', Ext.form.TextField);
58975 /**
58976  * @class Ext.form.TriggerField
58977  * @extends Ext.form.TextField
58978  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
58979  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
58980  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
58981  * for which you can provide a custom implementation.  For example:
58982  * <pre><code>
58983 var trigger = new Ext.form.TriggerField();
58984 trigger.onTriggerClick = myTriggerFn;
58985 trigger.applyToMarkup('my-field');
58986 </code></pre>
58987  *
58988  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
58989  * {@link Ext.form.DateField} and {@link Ext.form.ComboBox} are perfect examples of this.
58990  *
58991  * @constructor
58992  * Create a new TriggerField.
58993  * @param {Object} config Configuration options (valid {@Ext.form.TextField} config options will also be applied
58994  * to the base TextField)
58995  * @xtype trigger
58996  */
58997 Ext.form.TriggerField = Ext.extend(Ext.form.TextField,  {
58998     /**
58999      * @cfg {String} triggerClass
59000      * An additional CSS class used to style the trigger button.  The trigger will always get the
59001      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
59002      */
59003     /**
59004      * @cfg {Mixed} triggerConfig
59005      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the
59006      * trigger element for this Field. (Optional).</p>
59007      * <p>Specify this when you need a customized element to act as the trigger button for a TriggerField.</p>
59008      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing, positioning
59009      * and appearance of the trigger.  Defaults to:</p>
59010      * <pre><code>{tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass}</code></pre>
59011      */
59012     /**
59013      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
59014      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
59015      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
59016      * <pre><code>{tag: "input", type: "text", size: "16", autocomplete: "off"}</code></pre>
59017      */
59018     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
59019     /**
59020      * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
59021      * text field (defaults to <tt>false</tt>)
59022      */
59023     hideTrigger:false,
59024     /**
59025      * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field,
59026      * the field will only respond to a click on the trigger to set the value. (defaults to <tt>true</tt>).
59027      */
59028     editable: true,
59029     /**
59030      * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
59031      * hides the trigger.  Superceeds the editable and hideTrigger options if the value is true.
59032      * (defaults to <tt>false</tt>)
59033      */
59034     readOnly: false,
59035     /**
59036      * @cfg {String} wrapFocusClass The class added to the to the wrap of the trigger element. Defaults to
59037      * <tt>x-trigger-wrap-focus</tt>.
59038      */
59039     wrapFocusClass: 'x-trigger-wrap-focus',
59040     /**
59041      * @hide
59042      * @method autoSize
59043      */
59044     autoSize: Ext.emptyFn,
59045     // private
59046     monitorTab : true,
59047     // private
59048     deferHeight : true,
59049     // private
59050     mimicing : false,
59051
59052     actionMode: 'wrap',
59053
59054     defaultTriggerWidth: 17,
59055
59056     // private
59057     onResize : function(w, h){
59058         Ext.form.TriggerField.superclass.onResize.call(this, w, h);
59059         var tw = this.getTriggerWidth();
59060         if(Ext.isNumber(w)){
59061             this.el.setWidth(w - tw);
59062         }
59063         this.wrap.setWidth(this.el.getWidth() + tw);
59064     },
59065
59066     getTriggerWidth: function(){
59067         var tw = this.trigger.getWidth();
59068         if(!this.hideTrigger && !this.readOnly && tw === 0){
59069             tw = this.defaultTriggerWidth;
59070         }
59071         return tw;
59072     },
59073
59074     // private
59075     alignErrorIcon : function(){
59076         if(this.wrap){
59077             this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
59078         }
59079     },
59080
59081     // private
59082     onRender : function(ct, position){
59083         this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
59084         Ext.form.TriggerField.superclass.onRender.call(this, ct, position);
59085
59086         this.wrap = this.el.wrap({cls: 'x-form-field-wrap x-form-field-trigger-wrap'});
59087         this.trigger = this.wrap.createChild(this.triggerConfig ||
59088                 {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
59089         this.initTrigger();
59090         if(!this.width){
59091             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
59092         }
59093         this.resizeEl = this.positionEl = this.wrap;
59094     },
59095
59096     getWidth: function() {
59097         return(this.el.getWidth() + this.trigger.getWidth());
59098     },
59099
59100     updateEditState: function(){
59101         if(this.rendered){
59102             if (this.readOnly) {
59103                 this.el.dom.readOnly = true;
59104                 this.el.addClass('x-trigger-noedit');
59105                 this.mun(this.el, 'click', this.onTriggerClick, this);
59106                 this.trigger.setDisplayed(false);
59107             } else {
59108                 if (!this.editable) {
59109                     this.el.dom.readOnly = true;
59110                     this.el.addClass('x-trigger-noedit');
59111                     this.mon(this.el, 'click', this.onTriggerClick, this);
59112                 } else {
59113                     this.el.dom.readOnly = false;
59114                     this.el.removeClass('x-trigger-noedit');
59115                     this.mun(this.el, 'click', this.onTriggerClick, this);
59116                 }
59117                 this.trigger.setDisplayed(!this.hideTrigger);
59118             }
59119             this.onResize(this.width || this.wrap.getWidth());
59120         }
59121     },
59122
59123     setHideTrigger: function(hideTrigger){
59124         if(hideTrigger != this.hideTrigger){
59125             this.hideTrigger = hideTrigger;
59126             this.updateEditState();
59127         }
59128     },
59129
59130     /**
59131      * @param {Boolean} value True to allow the user to directly edit the field text
59132      * Allow or prevent the user from directly editing the field text.  If false is passed,
59133      * the user will only be able to modify the field using the trigger.  Will also add
59134      * a click event to the text field which will call the trigger. This method
59135      * is the runtime equivalent of setting the 'editable' config option at config time.
59136      */
59137     setEditable: function(editable){
59138         if(editable != this.editable){
59139             this.editable = editable;
59140             this.updateEditState();
59141         }
59142     },
59143
59144     /**
59145      * @param {Boolean} value True to prevent the user changing the field and explicitly
59146      * hide the trigger.
59147      * Setting this to true will superceed settings editable and hideTrigger.
59148      * Setting this to false will defer back to editable and hideTrigger. This method
59149      * is the runtime equivalent of setting the 'readOnly' config option at config time.
59150      */
59151     setReadOnly: function(readOnly){
59152         if(readOnly != this.readOnly){
59153             this.readOnly = readOnly;
59154             this.updateEditState();
59155         }
59156     },
59157
59158     afterRender : function(){
59159         Ext.form.TriggerField.superclass.afterRender.call(this);
59160         this.updateEditState();
59161     },
59162
59163     // private
59164     initTrigger : function(){
59165         this.mon(this.trigger, 'click', this.onTriggerClick, this, {preventDefault:true});
59166         this.trigger.addClassOnOver('x-form-trigger-over');
59167         this.trigger.addClassOnClick('x-form-trigger-click');
59168     },
59169
59170     // private
59171     onDestroy : function(){
59172         Ext.destroy(this.trigger, this.wrap);
59173         if (this.mimicing){
59174             this.doc.un('mousedown', this.mimicBlur, this);
59175         }
59176         delete this.doc;
59177         Ext.form.TriggerField.superclass.onDestroy.call(this);
59178     },
59179
59180     // private
59181     onFocus : function(){
59182         Ext.form.TriggerField.superclass.onFocus.call(this);
59183         if(!this.mimicing){
59184             this.wrap.addClass(this.wrapFocusClass);
59185             this.mimicing = true;
59186             this.doc.on('mousedown', this.mimicBlur, this, {delay: 10});
59187             if(this.monitorTab){
59188                 this.on('specialkey', this.checkTab, this);
59189             }
59190         }
59191     },
59192
59193     // private
59194     checkTab : function(me, e){
59195         if(e.getKey() == e.TAB){
59196             this.triggerBlur();
59197         }
59198     },
59199
59200     // private
59201     onBlur : Ext.emptyFn,
59202
59203     // private
59204     mimicBlur : function(e){
59205         if(!this.isDestroyed && !this.wrap.contains(e.target) && this.validateBlur(e)){
59206             this.triggerBlur();
59207         }
59208     },
59209
59210     // private
59211     triggerBlur : function(){
59212         this.mimicing = false;
59213         this.doc.un('mousedown', this.mimicBlur, this);
59214         if(this.monitorTab && this.el){
59215             this.un('specialkey', this.checkTab, this);
59216         }
59217         Ext.form.TriggerField.superclass.onBlur.call(this);
59218         if(this.wrap){
59219             this.wrap.removeClass(this.wrapFocusClass);
59220         }
59221     },
59222
59223     beforeBlur : Ext.emptyFn,
59224
59225     // private
59226     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
59227     validateBlur : function(e){
59228         return true;
59229     },
59230
59231     /**
59232      * The function that should handle the trigger's click event.  This method does nothing by default
59233      * until overridden by an implementing function.  See Ext.form.ComboBox and Ext.form.DateField for
59234      * sample implementations.
59235      * @method
59236      * @param {EventObject} e
59237      */
59238     onTriggerClick : Ext.emptyFn
59239
59240     /**
59241      * @cfg {Boolean} grow @hide
59242      */
59243     /**
59244      * @cfg {Number} growMin @hide
59245      */
59246     /**
59247      * @cfg {Number} growMax @hide
59248      */
59249 });
59250
59251 /**
59252  * @class Ext.form.TwinTriggerField
59253  * @extends Ext.form.TriggerField
59254  * TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
59255  * to be extended by an implementing class.  For an example of implementing this class, see the custom
59256  * SearchField implementation here:
59257  * <a href="http://extjs.com/deploy/ext/examples/form/custom.html">http://extjs.com/deploy/ext/examples/form/custom.html</a>
59258  */
59259 Ext.form.TwinTriggerField = Ext.extend(Ext.form.TriggerField, {
59260     /**
59261      * @cfg {Mixed} triggerConfig
59262      * <p>A {@link Ext.DomHelper DomHelper} config object specifying the structure of the trigger elements
59263      * for this Field. (Optional).</p>
59264      * <p>Specify this when you need a customized element to contain the two trigger elements for this Field.
59265      * Each trigger element must be marked by the CSS class <tt>x-form-trigger</tt> (also see
59266      * <tt>{@link #trigger1Class}</tt> and <tt>{@link #trigger2Class}</tt>).</p>
59267      * <p>Note that when using this option, it is the developer's responsibility to ensure correct sizing,
59268      * positioning and appearance of the triggers.</p>
59269      */
59270     /**
59271      * @cfg {String} trigger1Class
59272      * An additional CSS class used to style the trigger button.  The trigger will always get the
59273      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
59274      */
59275     /**
59276      * @cfg {String} trigger2Class
59277      * An additional CSS class used to style the trigger button.  The trigger will always get the
59278      * class <tt>'x-form-trigger'</tt> by default and <tt>triggerClass</tt> will be <b>appended</b> if specified.
59279      */
59280
59281     initComponent : function(){
59282         Ext.form.TwinTriggerField.superclass.initComponent.call(this);
59283
59284         this.triggerConfig = {
59285             tag:'span', cls:'x-form-twin-triggers', cn:[
59286             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
59287             {tag: "img", src: Ext.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
59288         ]};
59289     },
59290
59291     getTrigger : function(index){
59292         return this.triggers[index];
59293     },
59294
59295     initTrigger : function(){
59296         var ts = this.trigger.select('.x-form-trigger', true);
59297         var triggerField = this;
59298         ts.each(function(t, all, index){
59299             var triggerIndex = 'Trigger'+(index+1);
59300             t.hide = function(){
59301                 var w = triggerField.wrap.getWidth();
59302                 this.dom.style.display = 'none';
59303                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
59304                 this['hidden' + triggerIndex] = true;
59305             };
59306             t.show = function(){
59307                 var w = triggerField.wrap.getWidth();
59308                 this.dom.style.display = '';
59309                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
59310                 this['hidden' + triggerIndex] = false;
59311             };
59312
59313             if(this['hide'+triggerIndex]){
59314                 t.dom.style.display = 'none';
59315                 this['hidden' + triggerIndex] = true;
59316             }
59317             this.mon(t, 'click', this['on'+triggerIndex+'Click'], this, {preventDefault:true});
59318             t.addClassOnOver('x-form-trigger-over');
59319             t.addClassOnClick('x-form-trigger-click');
59320         }, this);
59321         this.triggers = ts.elements;
59322     },
59323
59324     getTriggerWidth: function(){
59325         var tw = 0;
59326         Ext.each(this.triggers, function(t, index){
59327             var triggerIndex = 'Trigger' + (index + 1),
59328                 w = t.getWidth();
59329             if(w === 0 && !this['hidden' + triggerIndex]){
59330                 tw += this.defaultTriggerWidth;
59331             }else{
59332                 tw += w;
59333             }
59334         }, this);
59335         return tw;
59336     },
59337
59338     // private
59339     onDestroy : function() {
59340         Ext.destroy(this.triggers);
59341         Ext.form.TwinTriggerField.superclass.onDestroy.call(this);
59342     },
59343
59344     /**
59345      * The function that should handle the trigger's click event.  This method does nothing by default
59346      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
59347      * for additional information.
59348      * @method
59349      * @param {EventObject} e
59350      */
59351     onTrigger1Click : Ext.emptyFn,
59352     /**
59353      * The function that should handle the trigger's click event.  This method does nothing by default
59354      * until overridden by an implementing function. See {@link Ext.form.TriggerField#onTriggerClick}
59355      * for additional information.
59356      * @method
59357      * @param {EventObject} e
59358      */
59359     onTrigger2Click : Ext.emptyFn
59360 });
59361 Ext.reg('trigger', Ext.form.TriggerField);
59362 /**
59363  * @class Ext.form.TextArea
59364  * @extends Ext.form.TextField
59365  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
59366  * support for auto-sizing.
59367  * @constructor
59368  * Creates a new TextArea
59369  * @param {Object} config Configuration options
59370  * @xtype textarea
59371  */
59372 Ext.form.TextArea = Ext.extend(Ext.form.TextField,  {
59373     /**
59374      * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
59375      * (defaults to <tt>60</tt>)
59376      */
59377     growMin : 60,
59378     /**
59379      * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.TextField#grow grow}=true</tt>
59380      * (defaults to <tt>1000</tt>)
59381      */
59382     growMax: 1000,
59383     growAppend : '&#160;\n&#160;',
59384
59385     enterIsSpecial : false,
59386
59387     /**
59388      * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
59389      * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to 
59390      * <tt>false</tt>.
59391      */
59392     preventScrollbars: false,
59393     /**
59394      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or true for a default
59395      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
59396      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
59397      * <pre><code>{tag: "textarea", style: "width:100px;height:60px;", autocomplete: "off"}</code></pre>
59398      */
59399
59400     // private
59401     onRender : function(ct, position){
59402         if(!this.el){
59403             this.defaultAutoCreate = {
59404                 tag: "textarea",
59405                 style:"width:100px;height:60px;",
59406                 autocomplete: "off"
59407             };
59408         }
59409         Ext.form.TextArea.superclass.onRender.call(this, ct, position);
59410         if(this.grow){
59411             this.textSizeEl = Ext.DomHelper.append(document.body, {
59412                 tag: "pre", cls: "x-form-grow-sizer"
59413             });
59414             if(this.preventScrollbars){
59415                 this.el.setStyle("overflow", "hidden");
59416             }
59417             this.el.setHeight(this.growMin);
59418         }
59419     },
59420
59421     onDestroy : function(){
59422         Ext.removeNode(this.textSizeEl);
59423         Ext.form.TextArea.superclass.onDestroy.call(this);
59424     },
59425
59426     fireKey : function(e){
59427         if(e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() != e.ENTER || e.hasModifier()))){
59428             this.fireEvent("specialkey", this, e);
59429         }
59430     },
59431     
59432     // private
59433     doAutoSize : function(e){
59434         return !e.isNavKeyPress() || e.getKey() == e.ENTER;
59435     },
59436
59437     /**
59438      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
59439      * This only takes effect if grow = true, and fires the {@link #autosize} event if the height changes.
59440      */
59441     autoSize: function(){
59442         if(!this.grow || !this.textSizeEl){
59443             return;
59444         }
59445         var el = this.el,
59446             v = Ext.util.Format.htmlEncode(el.dom.value),
59447             ts = this.textSizeEl,
59448             h;
59449             
59450         Ext.fly(ts).setWidth(this.el.getWidth());
59451         if(v.length < 1){
59452             v = "&#160;&#160;";
59453         }else{
59454             v += this.growAppend;
59455             if(Ext.isIE){
59456                 v = v.replace(/\n/g, '&#160;<br />');
59457             }
59458         }
59459         ts.innerHTML = v;
59460         h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
59461         if(h != this.lastHeight){
59462             this.lastHeight = h;
59463             this.el.setHeight(h);
59464             this.fireEvent("autosize", this, h);
59465         }
59466     }
59467 });
59468 Ext.reg('textarea', Ext.form.TextArea);/**
59469  * @class Ext.form.NumberField
59470  * @extends Ext.form.TextField
59471  * Numeric text field that provides automatic keystroke filtering and numeric validation.
59472  * @constructor
59473  * Creates a new NumberField
59474  * @param {Object} config Configuration options
59475  * @xtype numberfield
59476  */
59477 Ext.form.NumberField = Ext.extend(Ext.form.TextField,  {
59478     /**
59479      * @cfg {RegExp} stripCharsRe @hide
59480      */
59481     /**
59482      * @cfg {RegExp} maskRe @hide
59483      */
59484     /**
59485      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
59486      */
59487     fieldClass: "x-form-field x-form-num-field",
59488     /**
59489      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
59490      */
59491     allowDecimals : true,
59492     /**
59493      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
59494      */
59495     decimalSeparator : ".",
59496     /**
59497      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
59498      */
59499     decimalPrecision : 2,
59500     /**
59501      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
59502      */
59503     allowNegative : true,
59504     /**
59505      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
59506      */
59507     minValue : Number.NEGATIVE_INFINITY,
59508     /**
59509      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
59510      */
59511     maxValue : Number.MAX_VALUE,
59512     /**
59513      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
59514      */
59515     minText : "The minimum value for this field is {0}",
59516     /**
59517      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
59518      */
59519     maxText : "The maximum value for this field is {0}",
59520     /**
59521      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
59522      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
59523      */
59524     nanText : "{0} is not a valid number",
59525     /**
59526      * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
59527      */
59528     baseChars : "0123456789",
59529
59530     // private
59531     initEvents : function(){
59532         var allowed = this.baseChars + '';
59533         if (this.allowDecimals) {
59534             allowed += this.decimalSeparator;
59535         }
59536         if (this.allowNegative) {
59537             allowed += '-';
59538         }
59539         this.maskRe = new RegExp('[' + Ext.escapeRe(allowed) + ']');
59540         Ext.form.NumberField.superclass.initEvents.call(this);
59541     },
59542     
59543     /**
59544      * Runs all of NumberFields validations and returns an array of any errors. Note that this first
59545      * runs TextField's validations, so the returned array is an amalgamation of all field errors.
59546      * The additional validations run test that the value is a number, and that it is within the
59547      * configured min and max values.
59548      * @param {Mixed} value The value to get errors for (defaults to the current field value)
59549      * @return {Array} All validation errors for this field
59550      */
59551     getErrors: function(value) {
59552         var errors = Ext.form.NumberField.superclass.getErrors.apply(this, arguments);
59553         
59554         value = value || this.processValue(this.getRawValue());
59555         
59556         if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
59557              return errors;
59558         }
59559         
59560         value = String(value).replace(this.decimalSeparator, ".");
59561         
59562         if(isNaN(value)){
59563             errors.push(String.format(this.nanText, value));
59564         }
59565         
59566         var num = this.parseValue(value);
59567         
59568         if(num < this.minValue){
59569             errors.push(String.format(this.minText, this.minValue));
59570         }
59571         
59572         if(num > this.maxValue){
59573             errors.push(String.format(this.maxText, this.maxValue));
59574         }
59575         
59576         return errors;
59577     },
59578
59579     getValue : function(){
59580         return this.fixPrecision(this.parseValue(Ext.form.NumberField.superclass.getValue.call(this)));
59581     },
59582
59583     setValue : function(v){
59584         v = Ext.isNumber(v) ? v : parseFloat(String(v).replace(this.decimalSeparator, "."));
59585         v = isNaN(v) ? '' : String(v).replace(".", this.decimalSeparator);
59586         return Ext.form.NumberField.superclass.setValue.call(this, v);
59587     },
59588     
59589     /**
59590      * Replaces any existing {@link #minValue} with the new value.
59591      * @param {Number} value The minimum value
59592      */
59593     setMinValue : function(value){
59594         this.minValue = Ext.num(value, Number.NEGATIVE_INFINITY);
59595     },
59596     
59597     /**
59598      * Replaces any existing {@link #maxValue} with the new value.
59599      * @param {Number} value The maximum value
59600      */
59601     setMaxValue : function(value){
59602         this.maxValue = Ext.num(value, Number.MAX_VALUE);    
59603     },
59604
59605     // private
59606     parseValue : function(value){
59607         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
59608         return isNaN(value) ? '' : value;
59609     },
59610
59611     // private
59612     fixPrecision : function(value){
59613         var nan = isNaN(value);
59614         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
59615            return nan ? '' : value;
59616         }
59617         return parseFloat(parseFloat(value).toFixed(this.decimalPrecision));
59618     },
59619
59620     beforeBlur : function(){
59621         var v = this.parseValue(this.getRawValue());
59622         if(!Ext.isEmpty(v)){
59623             this.setValue(this.fixPrecision(v));
59624         }
59625     }
59626 });
59627 Ext.reg('numberfield', Ext.form.NumberField);/**
59628  * @class Ext.form.DateField
59629  * @extends Ext.form.TriggerField
59630  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.
59631  * @constructor
59632  * Create a new DateField
59633  * @param {Object} config
59634  * @xtype datefield
59635  */
59636 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {
59637     /**
59638      * @cfg {String} format
59639      * The default date format string which can be overriden for localization support.  The format must be
59640      * valid according to {@link Date#parseDate} (defaults to <tt>'m/d/Y'</tt>).
59641      */
59642     format : "m/d/Y",
59643     /**
59644      * @cfg {String} altFormats
59645      * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
59646      * does not match the defined format (defaults to
59647      * <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>).
59648      */
59649     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",
59650     /**
59651      * @cfg {String} disabledDaysText
59652      * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
59653      */
59654     disabledDaysText : "Disabled",
59655     /**
59656      * @cfg {String} disabledDatesText
59657      * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
59658      */
59659     disabledDatesText : "Disabled",
59660     /**
59661      * @cfg {String} minText
59662      * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
59663      * <tt>'The date in this field must be after {minValue}'</tt>).
59664      */
59665     minText : "The date in this field must be equal to or after {0}",
59666     /**
59667      * @cfg {String} maxText
59668      * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
59669      * <tt>'The date in this field must be before {maxValue}'</tt>).
59670      */
59671     maxText : "The date in this field must be equal to or before {0}",
59672     /**
59673      * @cfg {String} invalidText
59674      * The error text to display when the date in the field is invalid (defaults to
59675      * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
59676      */
59677     invalidText : "{0} is not a valid date - it must be in the format {1}",
59678     /**
59679      * @cfg {String} triggerClass
59680      * An additional CSS class used to style the trigger button.  The trigger will always get the
59681      * class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
59682      * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
59683      */
59684     triggerClass : 'x-form-date-trigger',
59685     /**
59686      * @cfg {Boolean} showToday
59687      * <tt>false</tt> to hide the footer area of the DatePicker containing the Today button and disable
59688      * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
59689      */
59690     showToday : true,
59691     /**
59692      * @cfg {Date/String} minValue
59693      * The minimum allowed date. Can be either a Javascript date object or a string date in a
59694      * valid format (defaults to null).
59695      */
59696     /**
59697      * @cfg {Date/String} maxValue
59698      * The maximum allowed date. Can be either a Javascript date object or a string date in a
59699      * valid format (defaults to null).
59700      */
59701     /**
59702      * @cfg {Array} disabledDays
59703      * An array of days to disable, 0 based (defaults to null). Some examples:<pre><code>
59704 // disable Sunday and Saturday:
59705 disabledDays:  [0, 6]
59706 // disable weekdays:
59707 disabledDays: [1,2,3,4,5]
59708      * </code></pre>
59709      */
59710     /**
59711      * @cfg {Array} disabledDates
59712      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
59713      * expression so they are very powerful. Some examples:<pre><code>
59714 // disable these exact dates:
59715 disabledDates: ["03/08/2003", "09/16/2003"]
59716 // disable these days for every year:
59717 disabledDates: ["03/08", "09/16"]
59718 // only match the beginning (useful if you are using short years):
59719 disabledDates: ["^03/08"]
59720 // disable every day in March 2006:
59721 disabledDates: ["03/../2006"]
59722 // disable every day in every March:
59723 disabledDates: ["^03"]
59724      * </code></pre>
59725      * Note that the format of the dates included in the array should exactly match the {@link #format} config.
59726      * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
59727      * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
59728      */
59729     /**
59730      * @cfg {String/Object} autoCreate
59731      * A {@link Ext.DomHelper DomHelper element specification object}, or <tt>true</tt> for the default element
59732      * specification object:<pre><code>
59733      * autoCreate: {tag: "input", type: "text", size: "10", autocomplete: "off"}
59734      * </code></pre>
59735      */
59736
59737     // private
59738     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
59739
59740     // in the absence of a time value, a default value of 12 noon will be used
59741     // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
59742     initTime: '12', // 24 hour format
59743
59744     initTimeFormat: 'H',
59745
59746     // PUBLIC -- to be documented
59747     safeParse : function(value, format) {
59748         if (/[gGhH]/.test(format.replace(/(\\.)/g, ''))) {
59749             // if parse format contains hour information, no DST adjustment is necessary
59750             return Date.parseDate(value, format);
59751         } else {
59752             // set time to 12 noon, then clear the time
59753             var parsedDate = Date.parseDate(value + ' ' + this.initTime, format + ' ' + this.initTimeFormat);
59754             
59755             if (parsedDate) return parsedDate.clearTime();
59756         }
59757     },
59758
59759     initComponent : function(){
59760         Ext.form.DateField.superclass.initComponent.call(this);
59761
59762         this.addEvents(
59763             /**
59764              * @event select
59765              * Fires when a date is selected via the date picker.
59766              * @param {Ext.form.DateField} this
59767              * @param {Date} date The date that was selected
59768              */
59769             'select'
59770         );
59771
59772         if(Ext.isString(this.minValue)){
59773             this.minValue = this.parseDate(this.minValue);
59774         }
59775         if(Ext.isString(this.maxValue)){
59776             this.maxValue = this.parseDate(this.maxValue);
59777         }
59778         this.disabledDatesRE = null;
59779         this.initDisabledDays();
59780     },
59781
59782     initEvents: function() {
59783         Ext.form.DateField.superclass.initEvents.call(this);
59784         this.keyNav = new Ext.KeyNav(this.el, {
59785             "down": function(e) {
59786                 this.onTriggerClick();
59787             },
59788             scope: this,
59789             forceKeyDown: true
59790         });
59791     },
59792
59793
59794     // private
59795     initDisabledDays : function(){
59796         if(this.disabledDates){
59797             var dd = this.disabledDates,
59798                 len = dd.length - 1,
59799                 re = "(?:";
59800
59801             Ext.each(dd, function(d, i){
59802                 re += Ext.isDate(d) ? '^' + Ext.escapeRe(d.dateFormat(this.format)) + '$' : dd[i];
59803                 if(i != len){
59804                     re += '|';
59805                 }
59806             }, this);
59807             this.disabledDatesRE = new RegExp(re + ')');
59808         }
59809     },
59810
59811     /**
59812      * Replaces any existing disabled dates with new values and refreshes the DatePicker.
59813      * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
59814      * for details on supported values) used to disable a pattern of dates.
59815      */
59816     setDisabledDates : function(dd){
59817         this.disabledDates = dd;
59818         this.initDisabledDays();
59819         if(this.menu){
59820             this.menu.picker.setDisabledDates(this.disabledDatesRE);
59821         }
59822     },
59823
59824     /**
59825      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
59826      * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
59827      * config for details on supported values.
59828      */
59829     setDisabledDays : function(dd){
59830         this.disabledDays = dd;
59831         if(this.menu){
59832             this.menu.picker.setDisabledDays(dd);
59833         }
59834     },
59835
59836     /**
59837      * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the DatePicker.
59838      * @param {Date} value The minimum date that can be selected
59839      */
59840     setMinValue : function(dt){
59841         this.minValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
59842         if(this.menu){
59843             this.menu.picker.setMinDate(this.minValue);
59844         }
59845     },
59846
59847     /**
59848      * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the DatePicker.
59849      * @param {Date} value The maximum date that can be selected
59850      */
59851     setMaxValue : function(dt){
59852         this.maxValue = (Ext.isString(dt) ? this.parseDate(dt) : dt);
59853         if(this.menu){
59854             this.menu.picker.setMaxDate(this.maxValue);
59855         }
59856     },
59857     
59858     /**
59859      * Runs all of NumberFields validations and returns an array of any errors. Note that this first
59860      * runs TextField's validations, so the returned array is an amalgamation of all field errors.
59861      * The additional validation checks are testing that the date format is valid, that the chosen
59862      * date is within the min and max date constraints set, that the date chosen is not in the disabledDates
59863      * regex and that the day chosed is not one of the disabledDays.
59864      * @param {Mixed} value The value to get errors for (defaults to the current field value)
59865      * @return {Array} All validation errors for this field
59866      */
59867     getErrors: function(value) {
59868         var errors = Ext.form.DateField.superclass.getErrors.apply(this, arguments);
59869         
59870         value = this.formatDate(value || this.processValue(this.getRawValue()));
59871         
59872         if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
59873              return errors;
59874         }
59875         
59876         var svalue = value;
59877         value = this.parseDate(value);
59878         if (!value) {
59879             errors.push(String.format(this.invalidText, svalue, this.format));
59880             return errors;
59881         }
59882         
59883         var time = value.getTime();
59884         if (this.minValue && time < this.minValue.getTime()) {
59885             errors.push(String.format(this.minText, this.formatDate(this.minValue)));
59886         }
59887         
59888         if (this.maxValue && time > this.maxValue.getTime()) {
59889             errors.push(String.format(this.maxText, this.formatDate(this.maxValue)));
59890         }
59891         
59892         if (this.disabledDays) {
59893             var day = value.getDay();
59894             
59895             for(var i = 0; i < this.disabledDays.length; i++) {
59896                 if (day === this.disabledDays[i]) {
59897                     errors.push(this.disabledDaysText);
59898                     break;
59899                 }
59900             }
59901         }
59902         
59903         var fvalue = this.formatDate(value);
59904         if (this.disabledDatesRE && this.disabledDatesRE.test(fvalue)) {
59905             errors.push(String.format(this.disabledDatesText, fvalue));
59906         }
59907         
59908         return errors;
59909     },
59910
59911     // private
59912     // Provides logic to override the default TriggerField.validateBlur which just returns true
59913     validateBlur : function(){
59914         return !this.menu || !this.menu.isVisible();
59915     },
59916
59917     /**
59918      * Returns the current date value of the date field.
59919      * @return {Date} The date value
59920      */
59921     getValue : function(){
59922         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";
59923     },
59924
59925     /**
59926      * Sets the value of the date field.  You can pass a date object or any string that can be
59927      * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
59928      * to the same rules as {@link Date#parseDate} (the default format used is <tt>"m/d/Y"</tt>).
59929      * <br />Usage:
59930      * <pre><code>
59931 //All of these calls set the same date value (May 4, 2006)
59932
59933 //Pass a date object:
59934 var dt = new Date('5/4/2006');
59935 dateField.setValue(dt);
59936
59937 //Pass a date string (default format):
59938 dateField.setValue('05/04/2006');
59939
59940 //Pass a date string (custom format):
59941 dateField.format = 'Y-m-d';
59942 dateField.setValue('2006-05-04');
59943 </code></pre>
59944      * @param {String/Date} date The date or valid date string
59945      * @return {Ext.form.Field} this
59946      */
59947     setValue : function(date){
59948         return Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
59949     },
59950
59951     // private
59952     parseDate : function(value) {
59953         if(!value || Ext.isDate(value)){
59954             return value;
59955         }
59956
59957         var v = this.safeParse(value, this.format),
59958             af = this.altFormats,
59959             afa = this.altFormatsArray;
59960
59961         if (!v && af) {
59962             afa = afa || af.split("|");
59963
59964             for (var i = 0, len = afa.length; i < len && !v; i++) {
59965                 v = this.safeParse(value, afa[i]);
59966             }
59967         }
59968         return v;
59969     },
59970
59971     // private
59972     onDestroy : function(){
59973         Ext.destroy(this.menu, this.keyNav);
59974         Ext.form.DateField.superclass.onDestroy.call(this);
59975     },
59976
59977     // private
59978     formatDate : function(date){
59979         return Ext.isDate(date) ? date.dateFormat(this.format) : date;
59980     },
59981
59982     /**
59983      * @method onTriggerClick
59984      * @hide
59985      */
59986     // private
59987     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
59988     onTriggerClick : function(){
59989         if(this.disabled){
59990             return;
59991         }
59992         if(this.menu == null){
59993             this.menu = new Ext.menu.DateMenu({
59994                 hideOnClick: false,
59995                 focusOnSelect: false
59996             });
59997         }
59998         this.onFocus();
59999         Ext.apply(this.menu.picker,  {
60000             minDate : this.minValue,
60001             maxDate : this.maxValue,
60002             disabledDatesRE : this.disabledDatesRE,
60003             disabledDatesText : this.disabledDatesText,
60004             disabledDays : this.disabledDays,
60005             disabledDaysText : this.disabledDaysText,
60006             format : this.format,
60007             showToday : this.showToday,
60008             minText : String.format(this.minText, this.formatDate(this.minValue)),
60009             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
60010         });
60011         this.menu.picker.setValue(this.getValue() || new Date());
60012         this.menu.show(this.el, "tl-bl?");
60013         this.menuEvents('on');
60014     },
60015
60016     //private
60017     menuEvents: function(method){
60018         this.menu[method]('select', this.onSelect, this);
60019         this.menu[method]('hide', this.onMenuHide, this);
60020         this.menu[method]('show', this.onFocus, this);
60021     },
60022
60023     onSelect: function(m, d){
60024         this.setValue(d);
60025         this.fireEvent('select', this, d);
60026         this.menu.hide();
60027     },
60028
60029     onMenuHide: function(){
60030         this.focus(false, 60);
60031         this.menuEvents('un');
60032     },
60033
60034     // private
60035     beforeBlur : function(){
60036         var v = this.parseDate(this.getRawValue());
60037         if(v){
60038             this.setValue(v);
60039         }
60040     }
60041
60042     /**
60043      * @cfg {Boolean} grow @hide
60044      */
60045     /**
60046      * @cfg {Number} growMin @hide
60047      */
60048     /**
60049      * @cfg {Number} growMax @hide
60050      */
60051     /**
60052      * @hide
60053      * @method autoSize
60054      */
60055 });
60056 Ext.reg('datefield', Ext.form.DateField);/**
60057  * @class Ext.form.DisplayField
60058  * @extends Ext.form.Field
60059  * A display-only text field which is not validated and not submitted.
60060  * @constructor
60061  * Creates a new DisplayField.
60062  * @param {Object} config Configuration options
60063  * @xtype displayfield
60064  */
60065 Ext.form.DisplayField = Ext.extend(Ext.form.Field,  {
60066     validationEvent : false,
60067     validateOnBlur : false,
60068     defaultAutoCreate : {tag: "div"},
60069     /**
60070      * @cfg {String} fieldClass The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)
60071      */
60072     fieldClass : "x-form-display-field",
60073     /**
60074      * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to
60075      * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than
60076      * rendering them as string literals per the default logic.
60077      */
60078     htmlEncode: false,
60079
60080     // private
60081     initEvents : Ext.emptyFn,
60082
60083     isValid : function(){
60084         return true;
60085     },
60086
60087     validate : function(){
60088         return true;
60089     },
60090
60091     getRawValue : function(){
60092         var v = this.rendered ? this.el.dom.innerHTML : Ext.value(this.value, '');
60093         if(v === this.emptyText){
60094             v = '';
60095         }
60096         if(this.htmlEncode){
60097             v = Ext.util.Format.htmlDecode(v);
60098         }
60099         return v;
60100     },
60101
60102     getValue : function(){
60103         return this.getRawValue();
60104     },
60105     
60106     getName: function() {
60107         return this.name;
60108     },
60109
60110     setRawValue : function(v){
60111         if(this.htmlEncode){
60112             v = Ext.util.Format.htmlEncode(v);
60113         }
60114         return this.rendered ? (this.el.dom.innerHTML = (Ext.isEmpty(v) ? '' : v)) : (this.value = v);
60115     },
60116
60117     setValue : function(v){
60118         this.setRawValue(v);
60119         return this;
60120     }
60121     /** 
60122      * @cfg {String} inputType 
60123      * @hide
60124      */
60125     /** 
60126      * @cfg {Boolean} disabled 
60127      * @hide
60128      */
60129     /** 
60130      * @cfg {Boolean} readOnly 
60131      * @hide
60132      */
60133     /** 
60134      * @cfg {Boolean} validateOnBlur 
60135      * @hide
60136      */
60137     /** 
60138      * @cfg {Number} validationDelay 
60139      * @hide
60140      */
60141     /** 
60142      * @cfg {String/Boolean} validationEvent 
60143      * @hide
60144      */
60145 });
60146
60147 Ext.reg('displayfield', Ext.form.DisplayField);
60148 /**
60149  * @class Ext.form.ComboBox
60150  * @extends Ext.form.TriggerField
60151  * <p>A combobox control with support for autocomplete, remote-loading, paging and many other features.</p>
60152  * <p>A ComboBox works in a similar manner to a traditional HTML &lt;select> field. The difference is
60153  * that to submit the {@link #valueField}, you must specify a {@link #hiddenName} to create a hidden input
60154  * field to hold the value of the valueField. The <i>{@link #displayField}</i> is shown in the text field
60155  * which is named according to the {@link #name}.</p>
60156  * <p><b><u>Events</u></b></p>
60157  * <p>To do something when something in ComboBox is selected, configure the select event:<pre><code>
60158 var cb = new Ext.form.ComboBox({
60159     // all of your config options
60160     listeners:{
60161          scope: yourScope,
60162          'select': yourFunction
60163     }
60164 });
60165
60166 // Alternatively, you can assign events after the object is created:
60167 var cb = new Ext.form.ComboBox(yourOptions);
60168 cb.on('select', yourFunction, yourScope);
60169  * </code></pre></p>
60170  *
60171  * <p><b><u>ComboBox in Grid</u></b></p>
60172  * <p>If using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a {@link Ext.grid.Column#renderer renderer}
60173  * will be needed to show the displayField when the editor is not active.  Set up the renderer manually, or implement
60174  * a reusable render, for example:<pre><code>
60175 // create reusable renderer
60176 Ext.util.Format.comboRenderer = function(combo){
60177     return function(value){
60178         var record = combo.findRecord(combo.{@link #valueField}, value);
60179         return record ? record.get(combo.{@link #displayField}) : combo.{@link #valueNotFoundText};
60180     }
60181 }
60182
60183 // create the combo instance
60184 var combo = new Ext.form.ComboBox({
60185     {@link #typeAhead}: true,
60186     {@link #triggerAction}: 'all',
60187     {@link #lazyRender}:true,
60188     {@link #mode}: 'local',
60189     {@link #store}: new Ext.data.ArrayStore({
60190         id: 0,
60191         fields: [
60192             'myId',
60193             'displayText'
60194         ],
60195         data: [[1, 'item1'], [2, 'item2']]
60196     }),
60197     {@link #valueField}: 'myId',
60198     {@link #displayField}: 'displayText'
60199 });
60200
60201 // snippet of column model used within grid
60202 var cm = new Ext.grid.ColumnModel([{
60203        ...
60204     },{
60205        header: "Some Header",
60206        dataIndex: 'whatever',
60207        width: 130,
60208        editor: combo, // specify reference to combo instance
60209        renderer: Ext.util.Format.comboRenderer(combo) // pass combo instance to reusable renderer
60210     },
60211     ...
60212 ]);
60213  * </code></pre></p>
60214  *
60215  * <p><b><u>Filtering</u></b></p>
60216  * <p>A ComboBox {@link #doQuery uses filtering itself}, for information about filtering the ComboBox
60217  * store manually see <tt>{@link #lastQuery}</tt>.</p>
60218  * @constructor
60219  * Create a new ComboBox.
60220  * @param {Object} config Configuration options
60221  * @xtype combo
60222  */
60223 Ext.form.ComboBox = Ext.extend(Ext.form.TriggerField, {
60224     /**
60225      * @cfg {Mixed} transform The id, DOM node or element of an existing HTML SELECT to convert to a ComboBox.
60226      * Note that if you specify this and the combo is going to be in an {@link Ext.form.BasicForm} or
60227      * {@link Ext.form.FormPanel}, you must also set <tt>{@link #lazyRender} = true</tt>.
60228      */
60229     /**
60230      * @cfg {Boolean} lazyRender <tt>true</tt> to prevent the ComboBox from rendering until requested
60231      * (should always be used when rendering into an {@link Ext.Editor} (e.g. {@link Ext.grid.EditorGridPanel Grids}),
60232      * defaults to <tt>false</tt>).
60233      */
60234     /**
60235      * @cfg {String/Object} autoCreate <p>A {@link Ext.DomHelper DomHelper} element spec, or <tt>true</tt> for a default
60236      * element spec. Used to create the {@link Ext.Component#getEl Element} which will encapsulate this Component.
60237      * See <tt>{@link Ext.Component#autoEl autoEl}</tt> for details.  Defaults to:</p>
60238      * <pre><code>{tag: "input", type: "text", size: "24", autocomplete: "off"}</code></pre>
60239      */
60240     /**
60241      * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
60242      * Acceptable values for this property are:
60243      * <div class="mdetail-params"><ul>
60244      * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
60245      * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally,
60246      * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
60247      * <div class="mdetail-params"><ul>
60248      * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
60249      * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
60250      * {@link #valueField} and {@link #displayField})</div></li>
60251      * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
60252      * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
60253      * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
60254      * </div></li></ul></div></li></ul></div>
60255      * <p>See also <tt>{@link #mode}</tt>.</p>
60256      */
60257     /**
60258      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
60259      * the dropdown list (defaults to undefined, with no header element)
60260      */
60261
60262     // private
60263     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
60264     /**
60265      * @cfg {Number} listWidth The width (used as a parameter to {@link Ext.Element#setWidth}) of the dropdown
60266      * list (defaults to the width of the ComboBox field).  See also <tt>{@link #minListWidth}
60267      */
60268     /**
60269      * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
60270      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field1'</tt> if
60271      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
60272      * the store configuration}).
60273      * <p>See also <tt>{@link #valueField}</tt>.</p>
60274      * <p><b>Note</b>: if using a ComboBox in an {@link Ext.grid.EditorGridPanel Editor Grid} a
60275      * {@link Ext.grid.Column#renderer renderer} will be needed to show the displayField when the editor is not
60276      * active.</p>
60277      */
60278     /**
60279      * @cfg {String} valueField The underlying {@link Ext.data.Field#name data value name} to bind to this
60280      * ComboBox (defaults to undefined if <tt>{@link #mode} = 'remote'</tt> or <tt>'field2'</tt> if
60281      * {@link #transform transforming a select} or if the {@link #store field name is autogenerated based on
60282      * the store configuration}).
60283      * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
60284      * mapped.  See also <tt>{@link #hiddenName}</tt>, <tt>{@link #hiddenValue}</tt>, and <tt>{@link #displayField}</tt>.</p>
60285      */
60286     /**
60287      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
60288      * field's data value (defaults to the underlying DOM element's name). Required for the combo's value to automatically
60289      * post during a form submission.  See also {@link #valueField}.
60290      * <p><b>Note</b>: the hidden field's id will also default to this name if {@link #hiddenId} is not specified.
60291      * The ComboBox {@link Ext.Component#id id} and the <tt>{@link #hiddenId}</tt> <b>should be different</b>, since
60292      * no two DOM nodes should share the same id.  So, if the ComboBox <tt>{@link Ext.form.Field#name name}</tt> and
60293      * <tt>hiddenName</tt> are the same, you should specify a unique <tt>{@link #hiddenId}</tt>.</p>
60294      */
60295     /**
60296      * @cfg {String} hiddenId If <tt>{@link #hiddenName}</tt> is specified, <tt>hiddenId</tt> can also be provided
60297      * to give the hidden field a unique id (defaults to the <tt>{@link #hiddenName}</tt>).  The <tt>hiddenId</tt>
60298      * and combo {@link Ext.Component#id id} should be different, since no two DOM
60299      * nodes should share the same id.
60300      */
60301     /**
60302      * @cfg {String} hiddenValue Sets the initial value of the hidden field if {@link #hiddenName} is
60303      * specified to contain the selected {@link #valueField}, from the Store. Defaults to the configured
60304      * <tt>{@link Ext.form.Field#value value}</tt>.
60305      */
60306     /**
60307      * @cfg {String} listClass The CSS class to add to the predefined <tt>'x-combo-list'</tt> class
60308      * applied the dropdown list element (defaults to '').
60309      */
60310     listClass : '',
60311     /**
60312      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list
60313      * (defaults to <tt>'x-combo-selected'</tt>)
60314      */
60315     selectedClass : 'x-combo-selected',
60316     /**
60317      * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
60318      * (defaults to '')
60319      */
60320     listEmptyText: '',
60321     /**
60322      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
60323      * get the class <tt>'x-form-trigger'</tt> and <tt>triggerClass</tt> will be <b>appended</b> if specified
60324      * (defaults to <tt>'x-form-arrow-trigger'</tt> which displays a downward arrow icon).
60325      */
60326     triggerClass : 'x-form-arrow-trigger',
60327     /**
60328      * @cfg {Boolean/String} shadow <tt>true</tt> or <tt>"sides"</tt> for the default effect, <tt>"frame"</tt> for
60329      * 4-way shadow, and <tt>"drop"</tt> for bottom-right
60330      */
60331     shadow : 'sides',
60332     /**
60333      * @cfg {String/Array} listAlign A valid anchor position value. See <tt>{@link Ext.Element#alignTo}</tt> for details
60334      * on supported anchor positions and offsets. To specify x/y offsets as well, this value
60335      * may be specified as an Array of <tt>{@link Ext.Element#alignTo}</tt> method arguments.</p>
60336      * <pre><code>[ 'tl-bl?', [6,0] ]</code></pre>(defaults to <tt>'tl-bl?'</tt>)
60337      */
60338     listAlign : 'tl-bl?',
60339     /**
60340      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
60341      * (defaults to <tt>300</tt>)
60342      */
60343     maxHeight : 300,
60344     /**
60345      * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
60346      * distance to the viewport edges (defaults to <tt>90</tt>)
60347      */
60348     minHeight : 90,
60349     /**
60350      * @cfg {String} triggerAction The action to execute when the trigger is clicked.
60351      * <div class="mdetail-params"><ul>
60352      * <li><b><tt>'query'</tt></b> : <b>Default</b>
60353      * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.Field#getRawValue raw value}.</p></li>
60354      * <li><b><tt>'all'</tt></b> :
60355      * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
60356      * </ul></div>
60357      * <p>See also <code>{@link #queryParam}</code>.</p>
60358      */
60359     triggerAction : 'query',
60360     /**
60361      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
60362      * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #mode} = 'remote'</tt> or <tt>0</tt> if
60363      * <tt>{@link #mode} = 'local'</tt>, does not apply if
60364      * <tt>{@link Ext.form.TriggerField#editable editable} = false</tt>).
60365      */
60366     minChars : 4,
60367     /**
60368      * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
60369      * to <tt>true</tt>).  A false value would require a manual selection from the dropdown list to set the components value
60370      * unless the value of ({@link #typeAheadDelay}) were true.
60371      */
60372     autoSelect : true,
60373     /**
60374      * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
60375      * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
60376      * to <tt>false</tt>)
60377      */
60378     typeAhead : false,
60379     /**
60380      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
60381      * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #mode} = 'remote'</tt>
60382      * or <tt>10</tt> if <tt>{@link #mode} = 'local'</tt>)
60383      */
60384     queryDelay : 500,
60385     /**
60386      * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.PagingToolbar} is displayed in the
60387      * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
60388      * {@link Ext.PagingToolbar#pageSize limit} parameters. Only applies when <tt>{@link #mode} = 'remote'</tt>
60389      * (defaults to <tt>0</tt>).
60390      */
60391     pageSize : 0,
60392     /**
60393      * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
60394      * Only applies when <tt>{@link Ext.form.TriggerField#editable editable} = true</tt> (defaults to
60395      * <tt>false</tt>).
60396      */
60397     selectOnFocus : false,
60398     /**
60399      * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
60400      * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
60401      */
60402     queryParam : 'query',
60403     /**
60404      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
60405      * when <tt>{@link #mode} = 'remote'</tt> (defaults to <tt>'Loading...'</tt>)
60406      */
60407     loadingText : 'Loading...',
60408     /**
60409      * @cfg {Boolean} resizable <tt>true</tt> to add a resize handle to the bottom of the dropdown list
60410      * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
60411      * Defaults to <tt>false</tt>.
60412      */
60413     resizable : false,
60414     /**
60415      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
60416      * <tt>{@link #resizable} = true</tt> (defaults to <tt>8</tt>)
60417      */
60418     handleHeight : 8,
60419     /**
60420      * @cfg {String} allQuery The text query to send to the server to return all records for the list
60421      * with no filtering (defaults to '')
60422      */
60423     allQuery: '',
60424     /**
60425      * @cfg {String} mode Acceptable values are:
60426      * <div class="mdetail-params"><ul>
60427      * <li><b><tt>'remote'</tt></b> : <b>Default</b>
60428      * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
60429      * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
60430      * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
60431      * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
60432      * <li><b><tt>'local'</tt></b> :
60433      * <p class="sub-desc">ComboBox loads local data</p>
60434      * <pre><code>
60435 var combo = new Ext.form.ComboBox({
60436     renderTo: document.body,
60437     mode: 'local',
60438     store: new Ext.data.ArrayStore({
60439         id: 0,
60440         fields: [
60441             'myId',  // numeric value is the key
60442             'displayText'
60443         ],
60444         data: [[1, 'item1'], [2, 'item2']]  // data is local
60445     }),
60446     valueField: 'myId',
60447     displayField: 'displayText',
60448     triggerAction: 'all'
60449 });
60450      * </code></pre></li>
60451      * </ul></div>
60452      */
60453     mode: 'remote',
60454     /**
60455      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to <tt>70</tt>, will
60456      * be ignored if <tt>{@link #listWidth}</tt> has a higher value)
60457      */
60458     minListWidth : 70,
60459     /**
60460      * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
60461      * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
60462      */
60463     forceSelection : false,
60464     /**
60465      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
60466      * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
60467      */
60468     typeAheadDelay : 250,
60469     /**
60470      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
60471      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
60472      * default text is used, it means there is no value set and no validation will occur on this field.
60473      */
60474
60475     /**
60476      * @cfg {Boolean} lazyInit <tt>true</tt> to not initialize the list for this combo until the field is focused
60477      * (defaults to <tt>true</tt>)
60478      */
60479     lazyInit : true,
60480
60481     /**
60482      * @cfg {Boolean} clearFilterOnReset <tt>true</tt> to clear any filters on the store (when in local mode) when reset is called
60483      * (defaults to <tt>true</tt>)
60484      */
60485     clearFilterOnReset : true,
60486
60487     /**
60488      * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
60489      * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
60490      * Defaults to <tt>undefined</tt>.
60491      */
60492     submitValue: undefined,
60493
60494     /**
60495      * The value of the match string used to filter the store. Delete this property to force a requery.
60496      * Example use:
60497      * <pre><code>
60498 var combo = new Ext.form.ComboBox({
60499     ...
60500     mode: 'remote',
60501     ...
60502     listeners: {
60503         // delete the previous query in the beforequery event or set
60504         // combo.lastQuery = null (this will reload the store the next time it expands)
60505         beforequery: function(qe){
60506             delete qe.combo.lastQuery;
60507         }
60508     }
60509 });
60510      * </code></pre>
60511      * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
60512      * configure the combo with <tt>lastQuery=''</tt>. Example use:
60513      * <pre><code>
60514 var combo = new Ext.form.ComboBox({
60515     ...
60516     mode: 'local',
60517     triggerAction: 'all',
60518     lastQuery: ''
60519 });
60520      * </code></pre>
60521      * @property lastQuery
60522      * @type String
60523      */
60524
60525     // private
60526     initComponent : function(){
60527         Ext.form.ComboBox.superclass.initComponent.call(this);
60528         this.addEvents(
60529             /**
60530              * @event expand
60531              * Fires when the dropdown list is expanded
60532              * @param {Ext.form.ComboBox} combo This combo box
60533              */
60534             'expand',
60535             /**
60536              * @event collapse
60537              * Fires when the dropdown list is collapsed
60538              * @param {Ext.form.ComboBox} combo This combo box
60539              */
60540             'collapse',
60541
60542             /**
60543              * @event beforeselect
60544              * Fires before a list item is selected. Return false to cancel the selection.
60545              * @param {Ext.form.ComboBox} combo This combo box
60546              * @param {Ext.data.Record} record The data record returned from the underlying store
60547              * @param {Number} index The index of the selected item in the dropdown list
60548              */
60549             'beforeselect',
60550             /**
60551              * @event select
60552              * Fires when a list item is selected
60553              * @param {Ext.form.ComboBox} combo This combo box
60554              * @param {Ext.data.Record} record The data record returned from the underlying store
60555              * @param {Number} index The index of the selected item in the dropdown list
60556              */
60557             'select',
60558             /**
60559              * @event beforequery
60560              * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
60561              * cancel property to true.
60562              * @param {Object} queryEvent An object that has these properties:<ul>
60563              * <li><code>combo</code> : Ext.form.ComboBox <div class="sub-desc">This combo box</div></li>
60564              * <li><code>query</code> : String <div class="sub-desc">The query</div></li>
60565              * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
60566              * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
60567              * </ul>
60568              */
60569             'beforequery'
60570         );
60571         if(this.transform){
60572             var s = Ext.getDom(this.transform);
60573             if(!this.hiddenName){
60574                 this.hiddenName = s.name;
60575             }
60576             if(!this.store){
60577                 this.mode = 'local';
60578                 var d = [], opts = s.options;
60579                 for(var i = 0, len = opts.length;i < len; i++){
60580                     var o = opts[i],
60581                         value = (o.hasAttribute ? o.hasAttribute('value') : o.getAttributeNode('value').specified) ? o.value : o.text;
60582                     if(o.selected && Ext.isEmpty(this.value, true)) {
60583                         this.value = value;
60584                     }
60585                     d.push([value, o.text]);
60586                 }
60587                 this.store = new Ext.data.ArrayStore({
60588                     'id': 0,
60589                     fields: ['value', 'text'],
60590                     data : d,
60591                     autoDestroy: true
60592                 });
60593                 this.valueField = 'value';
60594                 this.displayField = 'text';
60595             }
60596             s.name = Ext.id(); // wipe out the name in case somewhere else they have a reference
60597             if(!this.lazyRender){
60598                 this.target = true;
60599                 this.el = Ext.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
60600                 this.render(this.el.parentNode, s);
60601             }
60602             Ext.removeNode(s);
60603         }
60604         //auto-configure store from local array data
60605         else if(this.store){
60606             this.store = Ext.StoreMgr.lookup(this.store);
60607             if(this.store.autoCreated){
60608                 this.displayField = this.valueField = 'field1';
60609                 if(!this.store.expandData){
60610                     this.displayField = 'field2';
60611                 }
60612                 this.mode = 'local';
60613             }
60614         }
60615
60616         this.selectedIndex = -1;
60617         if(this.mode == 'local'){
60618             if(!Ext.isDefined(this.initialConfig.queryDelay)){
60619                 this.queryDelay = 10;
60620             }
60621             if(!Ext.isDefined(this.initialConfig.minChars)){
60622                 this.minChars = 0;
60623             }
60624         }
60625     },
60626
60627     // private
60628     onRender : function(ct, position){
60629         if(this.hiddenName && !Ext.isDefined(this.submitValue)){
60630             this.submitValue = false;
60631         }
60632         Ext.form.ComboBox.superclass.onRender.call(this, ct, position);
60633         if(this.hiddenName){
60634             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName,
60635                     id: (this.hiddenId||this.hiddenName)}, 'before', true);
60636
60637         }
60638         if(Ext.isGecko){
60639             this.el.dom.setAttribute('autocomplete', 'off');
60640         }
60641
60642         if(!this.lazyInit){
60643             this.initList();
60644         }else{
60645             this.on('focus', this.initList, this, {single: true});
60646         }
60647     },
60648
60649     // private
60650     initValue : function(){
60651         Ext.form.ComboBox.superclass.initValue.call(this);
60652         if(this.hiddenField){
60653             this.hiddenField.value =
60654                 Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, '');
60655         }
60656     },
60657
60658     getParentZIndex : function(){
60659         var zindex;
60660         if (this.ownerCt){
60661             this.findParentBy(function(ct){
60662                 zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
60663                 return !!zindex;
60664             });
60665         }
60666         return zindex;
60667     },
60668
60669     // private
60670     initList : function(){
60671         if(!this.list){
60672             var cls = 'x-combo-list',
60673                 listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
60674                 zindex = parseInt(Ext.fly(listParent).getStyle('z-index'), 10);
60675
60676             if (!zindex) {
60677                 zindex = this.getParentZIndex();
60678             }
60679
60680             this.list = new Ext.Layer({
60681                 parentEl: listParent,
60682                 shadow: this.shadow,
60683                 cls: [cls, this.listClass].join(' '),
60684                 constrain:false,
60685                 zindex: (zindex || 12000) + 5
60686             });
60687
60688             var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
60689             this.list.setSize(lw, 0);
60690             this.list.swallowEvent('mousewheel');
60691             this.assetHeight = 0;
60692             if(this.syncFont !== false){
60693                 this.list.setStyle('font-size', this.el.getStyle('font-size'));
60694             }
60695             if(this.title){
60696                 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
60697                 this.assetHeight += this.header.getHeight();
60698             }
60699
60700             this.innerList = this.list.createChild({cls:cls+'-inner'});
60701             this.mon(this.innerList, 'mouseover', this.onViewOver, this);
60702             this.mon(this.innerList, 'mousemove', this.onViewMove, this);
60703             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
60704
60705             if(this.pageSize){
60706                 this.footer = this.list.createChild({cls:cls+'-ft'});
60707                 this.pageTb = new Ext.PagingToolbar({
60708                     store: this.store,
60709                     pageSize: this.pageSize,
60710                     renderTo:this.footer
60711                 });
60712                 this.assetHeight += this.footer.getHeight();
60713             }
60714
60715             if(!this.tpl){
60716                 /**
60717                 * @cfg {String/Ext.XTemplate} tpl <p>The template string, or {@link Ext.XTemplate} instance to
60718                 * use to display each item in the dropdown list. The dropdown list is displayed in a
60719                 * DataView. See {@link #view}.</p>
60720                 * <p>The default template string is:</p><pre><code>
60721                   '&lt;tpl for=".">&lt;div class="x-combo-list-item">{' + this.displayField + '}&lt;/div>&lt;/tpl>'
60722                 * </code></pre>
60723                 * <p>Override the default value to create custom UI layouts for items in the list.
60724                 * For example:</p><pre><code>
60725                   '&lt;tpl for=".">&lt;div ext:qtip="{state}. {nick}" class="x-combo-list-item">{state}&lt;/div>&lt;/tpl>'
60726                 * </code></pre>
60727                 * <p>The template <b>must</b> contain one or more substitution parameters using field
60728                 * names from the Combo's</b> {@link #store Store}. In the example above an
60729                 * <pre>ext:qtip</pre> attribute is added to display other fields from the Store.</p>
60730                 * <p>To preserve the default visual look of list items, add the CSS class name
60731                 * <pre>x-combo-list-item</pre> to the template's container element.</p>
60732                 * <p>Also see {@link #itemSelector} for additional details.</p>
60733                 */
60734                 this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
60735                 /**
60736                  * @cfg {String} itemSelector
60737                  * <p>A simple CSS selector (e.g. div.some-class or span:first-child) that will be
60738                  * used to determine what nodes the {@link #view Ext.DataView} which handles the dropdown
60739                  * display will be working with.</p>
60740                  * <p><b>Note</b>: this setting is <b>required</b> if a custom XTemplate has been
60741                  * specified in {@link #tpl} which assigns a class other than <pre>'x-combo-list-item'</pre>
60742                  * to dropdown list items</b>
60743                  */
60744             }
60745
60746             /**
60747             * The {@link Ext.DataView DataView} used to display the ComboBox's options.
60748             * @type Ext.DataView
60749             */
60750             this.view = new Ext.DataView({
60751                 applyTo: this.innerList,
60752                 tpl: this.tpl,
60753                 singleSelect: true,
60754                 selectedClass: this.selectedClass,
60755                 itemSelector: this.itemSelector || '.' + cls + '-item',
60756                 emptyText: this.listEmptyText,
60757                 deferEmptyText: false
60758             });
60759
60760             this.mon(this.view, {
60761                 containerclick : this.onViewClick,
60762                 click : this.onViewClick,
60763                 scope :this
60764             });
60765
60766             this.bindStore(this.store, true);
60767
60768             if(this.resizable){
60769                 this.resizer = new Ext.Resizable(this.list,  {
60770                    pinned:true, handles:'se'
60771                 });
60772                 this.mon(this.resizer, 'resize', function(r, w, h){
60773                     this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
60774                     this.listWidth = w;
60775                     this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
60776                     this.restrictHeight();
60777                 }, this);
60778
60779                 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
60780             }
60781         }
60782     },
60783
60784     /**
60785      * <p>Returns the element used to house this ComboBox's pop-up list. Defaults to the document body.</p>
60786      * A custom implementation may be provided as a configuration option if the floating list needs to be rendered
60787      * to a different Element. An example might be rendering the list inside a Menu so that clicking
60788      * the list does not hide the Menu:<pre><code>
60789 var store = new Ext.data.ArrayStore({
60790     autoDestroy: true,
60791     fields: ['initials', 'fullname'],
60792     data : [
60793         ['FF', 'Fred Flintstone'],
60794         ['BR', 'Barney Rubble']
60795     ]
60796 });
60797
60798 var combo = new Ext.form.ComboBox({
60799     store: store,
60800     displayField: 'fullname',
60801     emptyText: 'Select a name...',
60802     forceSelection: true,
60803     getListParent: function() {
60804         return this.el.up('.x-menu');
60805     },
60806     iconCls: 'no-icon', //use iconCls if placing within menu to shift to right side of menu
60807     mode: 'local',
60808     selectOnFocus: true,
60809     triggerAction: 'all',
60810     typeAhead: true,
60811     width: 135
60812 });
60813
60814 var menu = new Ext.menu.Menu({
60815     id: 'mainMenu',
60816     items: [
60817         combo // A Field in a Menu
60818     ]
60819 });
60820 </code></pre>
60821      */
60822     getListParent : function() {
60823         return document.body;
60824     },
60825
60826     /**
60827      * Returns the store associated with this combo.
60828      * @return {Ext.data.Store} The store
60829      */
60830     getStore : function(){
60831         return this.store;
60832     },
60833
60834     // private
60835     bindStore : function(store, initial){
60836         if(this.store && !initial){
60837             if(this.store !== store && this.store.autoDestroy){
60838                 this.store.destroy();
60839             }else{
60840                 this.store.un('beforeload', this.onBeforeLoad, this);
60841                 this.store.un('load', this.onLoad, this);
60842                 this.store.un('exception', this.collapse, this);
60843             }
60844             if(!store){
60845                 this.store = null;
60846                 if(this.view){
60847                     this.view.bindStore(null);
60848                 }
60849                 if(this.pageTb){
60850                     this.pageTb.bindStore(null);
60851                 }
60852             }
60853         }
60854         if(store){
60855             if(!initial) {
60856                 this.lastQuery = null;
60857                 if(this.pageTb) {
60858                     this.pageTb.bindStore(store);
60859                 }
60860             }
60861
60862             this.store = Ext.StoreMgr.lookup(store);
60863             this.store.on({
60864                 scope: this,
60865                 beforeload: this.onBeforeLoad,
60866                 load: this.onLoad,
60867                 exception: this.collapse
60868             });
60869
60870             if(this.view){
60871                 this.view.bindStore(store);
60872             }
60873         }
60874     },
60875
60876     reset : function(){
60877         Ext.form.ComboBox.superclass.reset.call(this);
60878         if(this.clearFilterOnReset && this.mode == 'local'){
60879             this.store.clearFilter();
60880         }
60881     },
60882
60883     // private
60884     initEvents : function(){
60885         Ext.form.ComboBox.superclass.initEvents.call(this);
60886
60887         /**
60888          * @property keyNav
60889          * @type Ext.KeyNav
60890          * <p>A {@link Ext.KeyNav KeyNav} object which handles navigation keys for this ComboBox. This performs actions
60891          * based on keystrokes typed when the input field is focused.</p>
60892          * <p><b>After the ComboBox has been rendered</b>, you may override existing navigation key functionality,
60893          * or add your own based upon key names as specified in the {@link Ext.KeyNav KeyNav} class.</p>
60894          * <p>The function is executed in the scope (<code>this</code> reference of the ComboBox. Example:</p><pre><code>
60895 myCombo.keyNav.esc = function(e) {  // Override ESC handling function
60896     this.collapse();                // Standard behaviour of Ext's ComboBox.
60897     this.setValue(this.startValue); // We reset to starting value on ESC
60898 };
60899 myCombo.keyNav.tab = function() {   // Override TAB handling function
60900     this.onViewClick(false);        // Select the currently highlighted row
60901 };
60902 </code></pre>
60903          */
60904         this.keyNav = new Ext.KeyNav(this.el, {
60905             "up" : function(e){
60906                 this.inKeyMode = true;
60907                 this.selectPrev();
60908             },
60909
60910             "down" : function(e){
60911                 if(!this.isExpanded()){
60912                     this.onTriggerClick();
60913                 }else{
60914                     this.inKeyMode = true;
60915                     this.selectNext();
60916                 }
60917             },
60918
60919             "enter" : function(e){
60920                 this.onViewClick();
60921             },
60922
60923             "esc" : function(e){
60924                 this.collapse();
60925             },
60926
60927             "tab" : function(e){
60928                 if (this.forceSelection === true) {
60929                     this.collapse();
60930                 } else {
60931                     this.onViewClick(false);
60932                 }
60933                 return true;
60934             },
60935
60936             scope : this,
60937
60938             doRelay : function(e, h, hname){
60939                 if(hname == 'down' || this.scope.isExpanded()){
60940                     // this MUST be called before ComboBox#fireKey()
60941                     var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
60942                     if(!Ext.isIE && Ext.EventManager.useKeydown){
60943                         // call Combo#fireKey() for browsers which use keydown event (except IE)
60944                         this.scope.fireKey(e);
60945                     }
60946                     return relay;
60947                 }
60948                 return true;
60949             },
60950
60951             forceKeyDown : true,
60952             defaultEventAction: 'stopEvent'
60953         });
60954         this.queryDelay = Math.max(this.queryDelay || 10,
60955                 this.mode == 'local' ? 10 : 250);
60956         this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
60957         if(this.typeAhead){
60958             this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
60959         }
60960         if(!this.enableKeyEvents){
60961             this.mon(this.el, 'keyup', this.onKeyUp, this);
60962         }
60963     },
60964
60965
60966     // private
60967     onDestroy : function(){
60968         if (this.dqTask){
60969             this.dqTask.cancel();
60970             this.dqTask = null;
60971         }
60972         this.bindStore(null);
60973         Ext.destroy(
60974             this.resizer,
60975             this.view,
60976             this.pageTb,
60977             this.list
60978         );
60979         Ext.destroyMembers(this, 'hiddenField');
60980         Ext.form.ComboBox.superclass.onDestroy.call(this);
60981     },
60982
60983     // private
60984     fireKey : function(e){
60985         if (!this.isExpanded()) {
60986             Ext.form.ComboBox.superclass.fireKey.call(this, e);
60987         }
60988     },
60989
60990     // private
60991     onResize : function(w, h){
60992         Ext.form.ComboBox.superclass.onResize.apply(this, arguments);
60993         if(!isNaN(w) && this.isVisible() && this.list){
60994             this.doResize(w);
60995         }else{
60996             this.bufferSize = w;
60997         }
60998     },
60999
61000     doResize: function(w){
61001         if(!Ext.isDefined(this.listWidth)){
61002             var lw = Math.max(w, this.minListWidth);
61003             this.list.setWidth(lw);
61004             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
61005         }
61006     },
61007
61008     // private
61009     onEnable : function(){
61010         Ext.form.ComboBox.superclass.onEnable.apply(this, arguments);
61011         if(this.hiddenField){
61012             this.hiddenField.disabled = false;
61013         }
61014     },
61015
61016     // private
61017     onDisable : function(){
61018         Ext.form.ComboBox.superclass.onDisable.apply(this, arguments);
61019         if(this.hiddenField){
61020             this.hiddenField.disabled = true;
61021         }
61022     },
61023
61024     // private
61025     onBeforeLoad : function(){
61026         if(!this.hasFocus){
61027             return;
61028         }
61029         this.innerList.update(this.loadingText ?
61030                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
61031         this.restrictHeight();
61032         this.selectedIndex = -1;
61033     },
61034
61035     // private
61036     onLoad : function(){
61037         if(!this.hasFocus){
61038             return;
61039         }
61040         if(this.store.getCount() > 0 || this.listEmptyText){
61041             this.expand();
61042             this.restrictHeight();
61043             if(this.lastQuery == this.allQuery){
61044                 if(this.editable){
61045                     this.el.dom.select();
61046                 }
61047
61048                 if(this.autoSelect !== false && !this.selectByValue(this.value, true)){
61049                     this.select(0, true);
61050                 }
61051             }else{
61052                 if(this.autoSelect !== false){
61053                     this.selectNext();
61054                 }
61055                 if(this.typeAhead && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
61056                     this.taTask.delay(this.typeAheadDelay);
61057                 }
61058             }
61059         }else{
61060             this.collapse();
61061         }
61062
61063     },
61064
61065     // private
61066     onTypeAhead : function(){
61067         if(this.store.getCount() > 0){
61068             var r = this.store.getAt(0);
61069             var newValue = r.data[this.displayField];
61070             var len = newValue.length;
61071             var selStart = this.getRawValue().length;
61072             if(selStart != len){
61073                 this.setRawValue(newValue);
61074                 this.selectText(selStart, newValue.length);
61075             }
61076         }
61077     },
61078
61079     // private
61080     assertValue  : function(){
61081         var val = this.getRawValue(),
61082             rec = this.findRecord(this.displayField, val);
61083
61084         if(!rec && this.forceSelection){
61085             if(val.length > 0 && val != this.emptyText){
61086                 this.el.dom.value = Ext.value(this.lastSelectionText, '');
61087                 this.applyEmptyText();
61088             }else{
61089                 this.clearValue();
61090             }
61091         }else{
61092             if(rec){
61093                 // onSelect may have already set the value and by doing so
61094                 // set the display field properly.  Let's not wipe out the
61095                 // valueField here by just sending the displayField.
61096                 if (val == rec.get(this.displayField) && this.value == rec.get(this.valueField)){
61097                     return;
61098                 }
61099                 val = rec.get(this.valueField || this.displayField);
61100             }
61101             this.setValue(val);
61102         }
61103     },
61104
61105     // private
61106     onSelect : function(record, index){
61107         if(this.fireEvent('beforeselect', this, record, index) !== false){
61108             this.setValue(record.data[this.valueField || this.displayField]);
61109             this.collapse();
61110             this.fireEvent('select', this, record, index);
61111         }
61112     },
61113
61114     // inherit docs
61115     getName: function(){
61116         var hf = this.hiddenField;
61117         return hf && hf.name ? hf.name : this.hiddenName || Ext.form.ComboBox.superclass.getName.call(this);
61118     },
61119
61120     /**
61121      * Returns the currently selected field value or empty string if no value is set.
61122      * @return {String} value The selected value
61123      */
61124     getValue : function(){
61125         if(this.valueField){
61126             return Ext.isDefined(this.value) ? this.value : '';
61127         }else{
61128             return Ext.form.ComboBox.superclass.getValue.call(this);
61129         }
61130     },
61131
61132     /**
61133      * Clears any text/value currently set in the field
61134      */
61135     clearValue : function(){
61136         if(this.hiddenField){
61137             this.hiddenField.value = '';
61138         }
61139         this.setRawValue('');
61140         this.lastSelectionText = '';
61141         this.applyEmptyText();
61142         this.value = '';
61143     },
61144
61145     /**
61146      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
61147      * will be displayed in the field.  If the value does not match the data value of an existing item,
61148      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
61149      * Otherwise the field will be blank (although the value will still be set).
61150      * @param {String} value The value to match
61151      * @return {Ext.form.Field} this
61152      */
61153     setValue : function(v){
61154         var text = v;
61155         if(this.valueField){
61156             var r = this.findRecord(this.valueField, v);
61157             if(r){
61158                 text = r.data[this.displayField];
61159             }else if(Ext.isDefined(this.valueNotFoundText)){
61160                 text = this.valueNotFoundText;
61161             }
61162         }
61163         this.lastSelectionText = text;
61164         if(this.hiddenField){
61165             this.hiddenField.value = Ext.value(v, '');
61166         }
61167         Ext.form.ComboBox.superclass.setValue.call(this, text);
61168         this.value = v;
61169         return this;
61170     },
61171
61172     // private
61173     findRecord : function(prop, value){
61174         var record;
61175         if(this.store.getCount() > 0){
61176             this.store.each(function(r){
61177                 if(r.data[prop] == value){
61178                     record = r;
61179                     return false;
61180                 }
61181             });
61182         }
61183         return record;
61184     },
61185
61186     // private
61187     onViewMove : function(e, t){
61188         this.inKeyMode = false;
61189     },
61190
61191     // private
61192     onViewOver : function(e, t){
61193         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
61194             return;
61195         }
61196         var item = this.view.findItemFromChild(t);
61197         if(item){
61198             var index = this.view.indexOf(item);
61199             this.select(index, false);
61200         }
61201     },
61202
61203     // private
61204     onViewClick : function(doFocus){
61205         var index = this.view.getSelectedIndexes()[0],
61206             s = this.store,
61207             r = s.getAt(index);
61208         if(r){
61209             this.onSelect(r, index);
61210         }else {
61211             this.collapse();
61212         }
61213         if(doFocus !== false){
61214             this.el.focus();
61215         }
61216     },
61217
61218
61219     // private
61220     restrictHeight : function(){
61221         this.innerList.dom.style.height = '';
61222         var inner = this.innerList.dom,
61223             pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
61224             h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
61225             ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
61226             hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
61227             space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
61228
61229         h = Math.min(h, space, this.maxHeight);
61230
61231         this.innerList.setHeight(h);
61232         this.list.beginUpdate();
61233         this.list.setHeight(h+pad);
61234         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
61235         this.list.endUpdate();
61236     },
61237
61238     /**
61239      * Returns true if the dropdown list is expanded, else false.
61240      */
61241     isExpanded : function(){
61242         return this.list && this.list.isVisible();
61243     },
61244
61245     /**
61246      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
61247      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
61248      * @param {String} value The data value of the item to select
61249      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
61250      * selected item if it is not currently in view (defaults to true)
61251      * @return {Boolean} True if the value matched an item in the list, else false
61252      */
61253     selectByValue : function(v, scrollIntoView){
61254         if(!Ext.isEmpty(v, true)){
61255             var r = this.findRecord(this.valueField || this.displayField, v);
61256             if(r){
61257                 this.select(this.store.indexOf(r), scrollIntoView);
61258                 return true;
61259             }
61260         }
61261         return false;
61262     },
61263
61264     /**
61265      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
61266      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
61267      * @param {Number} index The zero-based index of the list item to select
61268      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
61269      * selected item if it is not currently in view (defaults to true)
61270      */
61271     select : function(index, scrollIntoView){
61272         this.selectedIndex = index;
61273         this.view.select(index);
61274         if(scrollIntoView !== false){
61275             var el = this.view.getNode(index);
61276             if(el){
61277                 this.innerList.scrollChildIntoView(el, false);
61278             }
61279         }
61280
61281     },
61282
61283     // private
61284     selectNext : function(){
61285         var ct = this.store.getCount();
61286         if(ct > 0){
61287             if(this.selectedIndex == -1){
61288                 this.select(0);
61289             }else if(this.selectedIndex < ct-1){
61290                 this.select(this.selectedIndex+1);
61291             }
61292         }
61293     },
61294
61295     // private
61296     selectPrev : function(){
61297         var ct = this.store.getCount();
61298         if(ct > 0){
61299             if(this.selectedIndex == -1){
61300                 this.select(0);
61301             }else if(this.selectedIndex !== 0){
61302                 this.select(this.selectedIndex-1);
61303             }
61304         }
61305     },
61306
61307     // private
61308     onKeyUp : function(e){
61309         var k = e.getKey();
61310         if(this.editable !== false && this.readOnly !== true && (k == e.BACKSPACE || !e.isSpecialKey())){
61311
61312             this.lastKey = k;
61313             this.dqTask.delay(this.queryDelay);
61314         }
61315         Ext.form.ComboBox.superclass.onKeyUp.call(this, e);
61316     },
61317
61318     // private
61319     validateBlur : function(){
61320         return !this.list || !this.list.isVisible();
61321     },
61322
61323     // private
61324     initQuery : function(){
61325         this.doQuery(this.getRawValue());
61326     },
61327
61328     // private
61329     beforeBlur : function(){
61330         this.assertValue();
61331     },
61332
61333     // private
61334     postBlur  : function(){
61335         Ext.form.ComboBox.superclass.postBlur.call(this);
61336         this.collapse();
61337         this.inKeyMode = false;
61338     },
61339
61340     /**
61341      * Execute a query to filter the dropdown list.  Fires the {@link #beforequery} event prior to performing the
61342      * query allowing the query action to be canceled if needed.
61343      * @param {String} query The SQL query to execute
61344      * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
61345      * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
61346      * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
61347      */
61348     doQuery : function(q, forceAll){
61349         q = Ext.isEmpty(q) ? '' : q;
61350         var qe = {
61351             query: q,
61352             forceAll: forceAll,
61353             combo: this,
61354             cancel:false
61355         };
61356         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
61357             return false;
61358         }
61359         q = qe.query;
61360         forceAll = qe.forceAll;
61361         if(forceAll === true || (q.length >= this.minChars)){
61362             if(this.lastQuery !== q){
61363                 this.lastQuery = q;
61364                 if(this.mode == 'local'){
61365                     this.selectedIndex = -1;
61366                     if(forceAll){
61367                         this.store.clearFilter();
61368                     }else{
61369                         this.store.filter(this.displayField, q);
61370                     }
61371                     this.onLoad();
61372                 }else{
61373                     this.store.baseParams[this.queryParam] = q;
61374                     this.store.load({
61375                         params: this.getParams(q)
61376                     });
61377                     this.expand();
61378                 }
61379             }else{
61380                 this.selectedIndex = -1;
61381                 this.onLoad();
61382             }
61383         }
61384     },
61385
61386     // private
61387     getParams : function(q){
61388         var p = {};
61389         //p[this.queryParam] = q;
61390         if(this.pageSize){
61391             p.start = 0;
61392             p.limit = this.pageSize;
61393         }
61394         return p;
61395     },
61396
61397     /**
61398      * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
61399      */
61400     collapse : function(){
61401         if(!this.isExpanded()){
61402             return;
61403         }
61404         this.list.hide();
61405         Ext.getDoc().un('mousewheel', this.collapseIf, this);
61406         Ext.getDoc().un('mousedown', this.collapseIf, this);
61407         this.fireEvent('collapse', this);
61408     },
61409
61410     // private
61411     collapseIf : function(e){
61412         if(!this.isDestroyed && !e.within(this.wrap) && !e.within(this.list)){
61413             this.collapse();
61414         }
61415     },
61416
61417     /**
61418      * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
61419      */
61420     expand : function(){
61421         if(this.isExpanded() || !this.hasFocus){
61422             return;
61423         }
61424
61425         if(this.title || this.pageSize){
61426             this.assetHeight = 0;
61427             if(this.title){
61428                 this.assetHeight += this.header.getHeight();
61429             }
61430             if(this.pageSize){
61431                 this.assetHeight += this.footer.getHeight();
61432             }
61433         }
61434
61435         if(this.bufferSize){
61436             this.doResize(this.bufferSize);
61437             delete this.bufferSize;
61438         }
61439         this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
61440
61441         // zindex can change, re-check it and set it if necessary
61442         var listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
61443             zindex = parseInt(Ext.fly(listParent).getStyle('z-index') ,10);
61444         if (!zindex){
61445             zindex = this.getParentZIndex();
61446         }
61447         if (zindex) {
61448             this.list.setZIndex(zindex + 5);
61449         }
61450         this.list.show();
61451         if(Ext.isGecko2){
61452             this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
61453         }
61454         this.mon(Ext.getDoc(), {
61455             scope: this,
61456             mousewheel: this.collapseIf,
61457             mousedown: this.collapseIf
61458         });
61459         this.fireEvent('expand', this);
61460     },
61461
61462     /**
61463      * @method onTriggerClick
61464      * @hide
61465      */
61466     // private
61467     // Implements the default empty TriggerField.onTriggerClick function
61468     onTriggerClick : function(){
61469         if(this.readOnly || this.disabled){
61470             return;
61471         }
61472         if(this.isExpanded()){
61473             this.collapse();
61474             this.el.focus();
61475         }else {
61476             this.onFocus({});
61477             if(this.triggerAction == 'all') {
61478                 this.doQuery(this.allQuery, true);
61479             } else {
61480                 this.doQuery(this.getRawValue());
61481             }
61482             this.el.focus();
61483         }
61484     }
61485
61486     /**
61487      * @hide
61488      * @method autoSize
61489      */
61490     /**
61491      * @cfg {Boolean} grow @hide
61492      */
61493     /**
61494      * @cfg {Number} growMin @hide
61495      */
61496     /**
61497      * @cfg {Number} growMax @hide
61498      */
61499
61500 });
61501 Ext.reg('combo', Ext.form.ComboBox);
61502 /**
61503  * @class Ext.form.Checkbox
61504  * @extends Ext.form.Field
61505  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
61506  * @constructor
61507  * Creates a new Checkbox
61508  * @param {Object} config Configuration options
61509  * @xtype checkbox
61510  */
61511 Ext.form.Checkbox = Ext.extend(Ext.form.Field,  {
61512     /**
61513      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
61514      */
61515     focusClass : undefined,
61516     /**
61517      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to 'x-form-field')
61518      */
61519     fieldClass : 'x-form-field',
61520     /**
61521      * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
61522      */
61523     checked : false,
61524     /**
61525      * @cfg {String} boxLabel The text that appears beside the checkbox
61526      */
61527     boxLabel: '&#160;',
61528     /**
61529      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
61530      * {tag: 'input', type: 'checkbox', autocomplete: 'off'})
61531      */
61532     defaultAutoCreate : { tag: 'input', type: 'checkbox', autocomplete: 'off'},
61533     /**
61534      * @cfg {String} boxLabel The text that appears beside the checkbox
61535      */
61536     /**
61537      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
61538      */
61539     /**
61540      * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of
61541      * handling the check event). The handler is passed the following parameters:
61542      * <div class="mdetail-params"><ul>
61543      * <li><b>checkbox</b> : Ext.form.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
61544      * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
61545      * </ul></div>
61546      */
61547     /**
61548      * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
61549      * (defaults to this Checkbox).
61550      */
61551
61552     // private
61553     actionMode : 'wrap',
61554
61555         // private
61556     initComponent : function(){
61557         Ext.form.Checkbox.superclass.initComponent.call(this);
61558         this.addEvents(
61559             /**
61560              * @event check
61561              * Fires when the checkbox is checked or unchecked.
61562              * @param {Ext.form.Checkbox} this This checkbox
61563              * @param {Boolean} checked The new checked value
61564              */
61565             'check'
61566         );
61567     },
61568
61569     // private
61570     onResize : function(){
61571         Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
61572         if(!this.boxLabel && !this.fieldLabel){
61573             this.el.alignTo(this.wrap, 'c-c');
61574         }
61575     },
61576
61577     // private
61578     initEvents : function(){
61579         Ext.form.Checkbox.superclass.initEvents.call(this);
61580         this.mon(this.el, {
61581             scope: this,
61582             click: this.onClick,
61583             change: this.onClick
61584         });
61585     },
61586
61587     /**
61588      * @hide
61589      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
61590      * @method
61591      */
61592     markInvalid : Ext.emptyFn,
61593     /**
61594      * @hide
61595      * Overridden and disabled. The editor element does not support standard valid/invalid marking.
61596      * @method
61597      */
61598     clearInvalid : Ext.emptyFn,
61599
61600     // private
61601     onRender : function(ct, position){
61602         Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
61603         if(this.inputValue !== undefined){
61604             this.el.dom.value = this.inputValue;
61605         }
61606         this.wrap = this.el.wrap({cls: 'x-form-check-wrap'});
61607         if(this.boxLabel){
61608             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
61609         }
61610         if(this.checked){
61611             this.setValue(true);
61612         }else{
61613             this.checked = this.el.dom.checked;
61614         }
61615         // Need to repaint for IE, otherwise positioning is broken
61616         if(Ext.isIE){
61617             this.wrap.repaint();
61618         }
61619         this.resizeEl = this.positionEl = this.wrap;
61620     },
61621
61622     // private
61623     onDestroy : function(){
61624         Ext.destroy(this.wrap);
61625         Ext.form.Checkbox.superclass.onDestroy.call(this);
61626     },
61627
61628     // private
61629     initValue : function() {
61630         this.originalValue = this.getValue();
61631     },
61632
61633     /**
61634      * Returns the checked state of the checkbox.
61635      * @return {Boolean} True if checked, else false
61636      */
61637     getValue : function(){
61638         if(this.rendered){
61639             return this.el.dom.checked;
61640         }
61641         return this.checked;
61642     },
61643
61644         // private
61645     onClick : function(){
61646         if(this.el.dom.checked != this.checked){
61647             this.setValue(this.el.dom.checked);
61648         }
61649     },
61650
61651     /**
61652      * Sets the checked state of the checkbox, fires the 'check' event, and calls a
61653      * <code>{@link #handler}</code> (if configured).
61654      * @param {Boolean/String} checked The following values will check the checkbox:
61655      * <code>true, 'true', '1', or 'on'</code>. Any other value will uncheck the checkbox.
61656      * @return {Ext.form.Field} this
61657      */
61658     setValue : function(v){
61659         var checked = this.checked ;
61660         this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
61661         if(this.rendered){
61662             this.el.dom.checked = this.checked;
61663             this.el.dom.defaultChecked = this.checked;
61664         }
61665         if(checked != this.checked){
61666             this.fireEvent('check', this, this.checked);
61667             if(this.handler){
61668                 this.handler.call(this.scope || this, this, this.checked);
61669             }
61670         }
61671         return this;
61672     }
61673 });
61674 Ext.reg('checkbox', Ext.form.Checkbox);
61675 /**
61676  * @class Ext.form.CheckboxGroup
61677  * @extends Ext.form.Field
61678  * <p>A grouping container for {@link Ext.form.Checkbox} controls.</p>
61679  * <p>Sample usage:</p>
61680  * <pre><code>
61681 var myCheckboxGroup = new Ext.form.CheckboxGroup({
61682     id:'myGroup',
61683     xtype: 'checkboxgroup',
61684     fieldLabel: 'Single Column',
61685     itemCls: 'x-check-group-alt',
61686     // Put all controls in a single column with width 100%
61687     columns: 1,
61688     items: [
61689         {boxLabel: 'Item 1', name: 'cb-col-1'},
61690         {boxLabel: 'Item 2', name: 'cb-col-2', checked: true},
61691         {boxLabel: 'Item 3', name: 'cb-col-3'}
61692     ]
61693 });
61694  * </code></pre>
61695  * @constructor
61696  * Creates a new CheckboxGroup
61697  * @param {Object} config Configuration options
61698  * @xtype checkboxgroup
61699  */
61700 Ext.form.CheckboxGroup = Ext.extend(Ext.form.Field, {
61701     /**
61702      * @cfg {Array} items An Array of {@link Ext.form.Checkbox Checkbox}es or Checkbox config objects
61703      * to arrange in the group.
61704      */
61705     /**
61706      * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
61707      * checkbox/radio controls using automatic layout.  This config can take several types of values:
61708      * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
61709      * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
61710      * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
61711      * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
61712      * <li><b>Array</b> : Object<p class="sub-desc">You can also specify an array of column widths, mixing integer
61713      * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
61714      * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
61715      * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
61716      * container you should do so.</p></li></ul>
61717      */
61718     columns : 'auto',
61719     /**
61720      * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
61721      * top to bottom before starting on the next column.  The number of controls in each column will be automatically
61722      * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
61723      * to columns one at a time, completely filling each row left to right before starting on the next row.
61724      */
61725     vertical : false,
61726     /**
61727      * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
61728      * If no items are selected at validation time, {@link @blankText} will be used as the error text.
61729      */
61730     allowBlank : true,
61731     /**
61732      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
61733      * select at least one item in this group")
61734      */
61735     blankText : "You must select at least one item in this group",
61736
61737     // private
61738     defaultType : 'checkbox',
61739
61740     // private
61741     groupCls : 'x-form-check-group',
61742
61743     // private
61744     initComponent: function(){
61745         this.addEvents(
61746             /**
61747              * @event change
61748              * Fires when the state of a child checkbox changes.
61749              * @param {Ext.form.CheckboxGroup} this
61750              * @param {Array} checked An array containing the checked boxes.
61751              */
61752             'change'
61753         );
61754         this.on('change', this.validate, this);
61755         Ext.form.CheckboxGroup.superclass.initComponent.call(this);
61756     },
61757
61758     // private
61759     onRender : function(ct, position){
61760         if(!this.el){
61761             var panelCfg = {
61762                 autoEl: {
61763                     id: this.id
61764                 },
61765                 cls: this.groupCls,
61766                 layout: 'column',
61767                 renderTo: ct,
61768                 bufferResize: false // Default this to false, since it doesn't really have a proper ownerCt.
61769             };
61770             var colCfg = {
61771                 xtype: 'container',
61772                 defaultType: this.defaultType,
61773                 layout: 'form',
61774                 defaults: {
61775                     hideLabel: true,
61776                     anchor: '100%'
61777                 }
61778             };
61779
61780             if(this.items[0].items){
61781
61782                 // The container has standard ColumnLayout configs, so pass them in directly
61783
61784                 Ext.apply(panelCfg, {
61785                     layoutConfig: {columns: this.items.length},
61786                     defaults: this.defaults,
61787                     items: this.items
61788                 });
61789                 for(var i=0, len=this.items.length; i<len; i++){
61790                     Ext.applyIf(this.items[i], colCfg);
61791                 }
61792
61793             }else{
61794
61795                 // The container has field item configs, so we have to generate the column
61796                 // panels first then move the items into the columns as needed.
61797
61798                 var numCols, cols = [];
61799
61800                 if(typeof this.columns == 'string'){ // 'auto' so create a col per item
61801                     this.columns = this.items.length;
61802                 }
61803                 if(!Ext.isArray(this.columns)){
61804                     var cs = [];
61805                     for(var i=0; i<this.columns; i++){
61806                         cs.push((100/this.columns)*.01); // distribute by even %
61807                     }
61808                     this.columns = cs;
61809                 }
61810
61811                 numCols = this.columns.length;
61812
61813                 // Generate the column configs with the correct width setting
61814                 for(var i=0; i<numCols; i++){
61815                     var cc = Ext.apply({items:[]}, colCfg);
61816                     cc[this.columns[i] <= 1 ? 'columnWidth' : 'width'] = this.columns[i];
61817                     if(this.defaults){
61818                         cc.defaults = Ext.apply(cc.defaults || {}, this.defaults);
61819                     }
61820                     cols.push(cc);
61821                 };
61822
61823                 // Distribute the original items into the columns
61824                 if(this.vertical){
61825                     var rows = Math.ceil(this.items.length / numCols), ri = 0;
61826                     for(var i=0, len=this.items.length; i<len; i++){
61827                         if(i>0 && i%rows==0){
61828                             ri++;
61829                         }
61830                         if(this.items[i].fieldLabel){
61831                             this.items[i].hideLabel = false;
61832                         }
61833                         cols[ri].items.push(this.items[i]);
61834                     };
61835                 }else{
61836                     for(var i=0, len=this.items.length; i<len; i++){
61837                         var ci = i % numCols;
61838                         if(this.items[i].fieldLabel){
61839                             this.items[i].hideLabel = false;
61840                         }
61841                         cols[ci].items.push(this.items[i]);
61842                     };
61843                 }
61844
61845                 Ext.apply(panelCfg, {
61846                     layoutConfig: {columns: numCols},
61847                     items: cols
61848                 });
61849             }
61850
61851             this.panel = new Ext.Container(panelCfg);
61852             this.panel.ownerCt = this;
61853             this.el = this.panel.getEl();
61854
61855             if(this.forId && this.itemCls){
61856                 var l = this.el.up(this.itemCls).child('label', true);
61857                 if(l){
61858                     l.setAttribute('htmlFor', this.forId);
61859                 }
61860             }
61861
61862             var fields = this.panel.findBy(function(c){
61863                 return c.isFormField;
61864             }, this);
61865
61866             this.items = new Ext.util.MixedCollection();
61867             this.items.addAll(fields);
61868         }
61869         Ext.form.CheckboxGroup.superclass.onRender.call(this, ct, position);
61870     },
61871
61872     initValue : function(){
61873         if(this.value){
61874             this.setValue.apply(this, this.buffered ? this.value : [this.value]);
61875             delete this.buffered;
61876             delete this.value;
61877         }
61878     },
61879
61880     afterRender : function(){
61881         Ext.form.CheckboxGroup.superclass.afterRender.call(this);
61882         this.eachItem(function(item){
61883             item.on('check', this.fireChecked, this);
61884             item.inGroup = true;
61885         });
61886     },
61887
61888     // private
61889     doLayout: function(){
61890         //ugly method required to layout hidden items
61891         if(this.rendered){
61892             this.panel.forceLayout = this.ownerCt.forceLayout;
61893             this.panel.doLayout();
61894         }
61895     },
61896
61897     // private
61898     fireChecked: function(){
61899         var arr = [];
61900         this.eachItem(function(item){
61901             if(item.checked){
61902                 arr.push(item);
61903             }
61904         });
61905         this.fireEvent('change', this, arr);
61906     },
61907     
61908     /**
61909      * Runs CheckboxGroup's validations and returns an array of any errors. The only error by default
61910      * is if allowBlank is set to true and no items are checked.
61911      * @return {Array} Array of all validation errors
61912      */
61913     getErrors: function() {
61914         var errors = Ext.form.CheckboxGroup.superclass.getErrors.apply(this, arguments);
61915         
61916         if (!this.allowBlank) {
61917             var blank = true;
61918             
61919             this.eachItem(function(f){
61920                 if (f.checked) {
61921                     return (blank = false);
61922                 }
61923             });
61924             
61925             if (blank) errors.push(this.blankText);
61926         }
61927         
61928         return errors;
61929     },
61930
61931     // private
61932     isDirty: function(){
61933         //override the behaviour to check sub items.
61934         if (this.disabled || !this.rendered) {
61935             return false;
61936         }
61937
61938         var dirty = false;
61939         
61940         this.eachItem(function(item){
61941             if(item.isDirty()){
61942                 dirty = true;
61943                 return false;
61944             }
61945         });
61946         
61947         return dirty;
61948     },
61949
61950     // private
61951     setReadOnly : function(readOnly){
61952         if(this.rendered){
61953             this.eachItem(function(item){
61954                 item.setReadOnly(readOnly);
61955             });
61956         }
61957         this.readOnly = readOnly;
61958     },
61959
61960     // private
61961     onDisable : function(){
61962         this.eachItem(function(item){
61963             item.disable();
61964         });
61965     },
61966
61967     // private
61968     onEnable : function(){
61969         this.eachItem(function(item){
61970             item.enable();
61971         });
61972     },
61973
61974     // private
61975     onResize : function(w, h){
61976         this.panel.setSize(w, h);
61977         this.panel.doLayout();
61978     },
61979
61980     // inherit docs from Field
61981     reset : function(){
61982         if (this.originalValue) {
61983             // Clear all items
61984             this.eachItem(function(c){
61985                 if(c.setValue){
61986                     c.setValue(false);
61987                     c.originalValue = c.getValue();
61988                 }
61989             });
61990             // Set items stored in originalValue, ugly - set a flag to reset the originalValue
61991             // during the horrible onSetValue.  This will allow trackResetOnLoad to function.
61992             this.resetOriginal = true;
61993             this.setValue(this.originalValue);
61994             delete this.resetOriginal;
61995         } else {
61996             this.eachItem(function(c){
61997                 if(c.reset){
61998                     c.reset();
61999                 }
62000             });
62001         }
62002         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
62003         // Important because reset is being called on both the group and the individual items.
62004         (function() {
62005             this.clearInvalid();
62006         }).defer(50, this);
62007     },
62008
62009     /**
62010      * {@link Ext.form.Checkbox#setValue Set the value(s)} of an item or items
62011      * in the group. Examples illustrating how this method may be called:
62012      * <pre><code>
62013 // call with name and value
62014 myCheckboxGroup.setValue('cb-col-1', true);
62015 // call with an array of boolean values
62016 myCheckboxGroup.setValue([true, false, false]);
62017 // call with an object literal specifying item:value pairs
62018 myCheckboxGroup.setValue({
62019     'cb-col-2': false,
62020     'cb-col-3': true
62021 });
62022 // use comma separated string to set items with name to true (checked)
62023 myCheckboxGroup.setValue('cb-col-1,cb-col-3');
62024      * </code></pre>
62025      * See {@link Ext.form.Checkbox#setValue} for additional information.
62026      * @param {Mixed} id The checkbox to check, or as described by example shown.
62027      * @param {Boolean} value (optional) The value to set the item.
62028      * @return {Ext.form.CheckboxGroup} this
62029      */
62030     setValue: function(){
62031         if(this.rendered){
62032             this.onSetValue.apply(this, arguments);
62033         }else{
62034             this.buffered = true;
62035             this.value = arguments;
62036         }
62037         return this;
62038     },
62039
62040     /**
62041      * @private
62042      * Sets the values of one or more of the items within the CheckboxGroup
62043      * @param {String|Array|Object} id Can take multiple forms. Can be optionally:
62044      * <ul>
62045      *   <li>An ID string to be used with a second argument</li>
62046      *   <li>An array of the form ['some', 'list', 'of', 'ids', 'to', 'mark', 'checked']</li>
62047      *   <li>An array in the form [true, true, false, true, false] etc, where each item relates to the check status of
62048      *       the checkbox at the same index</li>
62049      *   <li>An object containing ids of the checkboxes as keys and check values as properties</li>
62050      * </ul>
62051      * @param {String} value The value to set the field to if the first argument was a string
62052      */
62053     onSetValue: function(id, value){
62054         if(arguments.length == 1){
62055             if(Ext.isArray(id)){
62056                 Ext.each(id, function(val, idx){
62057                     if (Ext.isObject(val) && val.setValue){ // array of checkbox components to be checked
62058                         val.setValue(true);
62059                         if (this.resetOriginal === true) {
62060                             val.originalValue = val.getValue();
62061                         }
62062                     } else { // an array of boolean values
62063                         var item = this.items.itemAt(idx);
62064                         if(item){
62065                             item.setValue(val);
62066                         }
62067                     }
62068                 }, this);
62069             }else if(Ext.isObject(id)){
62070                 // set of name/value pairs
62071                 for(var i in id){
62072                     var f = this.getBox(i);
62073                     if(f){
62074                         f.setValue(id[i]);
62075                     }
62076                 }
62077             }else{
62078                 this.setValueForItem(id);
62079             }
62080         }else{
62081             var f = this.getBox(id);
62082             if(f){
62083                 f.setValue(value);
62084             }
62085         }
62086     },
62087
62088     // private
62089     beforeDestroy: function(){
62090         Ext.destroy(this.panel);
62091         Ext.form.CheckboxGroup.superclass.beforeDestroy.call(this);
62092
62093     },
62094
62095     setValueForItem : function(val){
62096         val = String(val).split(',');
62097         this.eachItem(function(item){
62098             if(val.indexOf(item.inputValue)> -1){
62099                 item.setValue(true);
62100             }
62101         });
62102     },
62103
62104     // private
62105     getBox : function(id){
62106         var box = null;
62107         this.eachItem(function(f){
62108             if(id == f || f.dataIndex == id || f.id == id || f.getName() == id){
62109                 box = f;
62110                 return false;
62111             }
62112         });
62113         return box;
62114     },
62115
62116     /**
62117      * Gets an array of the selected {@link Ext.form.Checkbox} in the group.
62118      * @return {Array} An array of the selected checkboxes.
62119      */
62120     getValue : function(){
62121         var out = [];
62122         this.eachItem(function(item){
62123             if(item.checked){
62124                 out.push(item);
62125             }
62126         });
62127         return out;
62128     },
62129
62130     /**
62131      * @private
62132      * Convenience function which passes the given function to every item in the composite
62133      * @param {Function} fn The function to call
62134      * @param {Object} scope Optional scope object
62135      */
62136     eachItem: function(fn, scope) {
62137         if(this.items && this.items.each){
62138             this.items.each(fn, scope || this);
62139         }
62140     },
62141
62142     /**
62143      * @cfg {String} name
62144      * @hide
62145      */
62146
62147     /**
62148      * @method getRawValue
62149      * @hide
62150      */
62151     getRawValue : Ext.emptyFn,
62152
62153     /**
62154      * @method setRawValue
62155      * @hide
62156      */
62157     setRawValue : Ext.emptyFn
62158
62159 });
62160
62161 Ext.reg('checkboxgroup', Ext.form.CheckboxGroup);
62162 /**
62163  * @class Ext.form.CompositeField
62164  * @extends Ext.form.Field
62165  * Composite field allowing a number of form Fields to be rendered on the same row. The fields are rendered
62166  * using an hbox layout internally, so all of the normal HBox layout config items are available. Example usage:
62167  * <pre>
62168 {
62169     xtype: 'compositefield',
62170     labelWidth: 120
62171     items: [
62172         {
62173             xtype     : 'textfield',
62174             fieldLabel: 'Title',
62175             width     : 20
62176         },
62177         {
62178             xtype     : 'textfield',
62179             fieldLabel: 'First',
62180             flex      : 1
62181         },
62182         {
62183             xtype     : 'textfield',
62184             fieldLabel: 'Last',
62185             flex      : 1
62186         }
62187     ]
62188 }
62189  * </pre>
62190  * In the example above the composite's fieldLabel will be set to 'Title, First, Last' as it groups the fieldLabels
62191  * of each of its children. This can be overridden by setting a fieldLabel on the compositefield itself:
62192  * <pre>
62193 {
62194     xtype: 'compositefield',
62195     fieldLabel: 'Custom label',
62196     items: [...]
62197 }
62198  * </pre>
62199  * Any Ext.form.* component can be placed inside a composite field.
62200  */
62201 Ext.form.CompositeField = Ext.extend(Ext.form.Field, {
62202
62203     /**
62204      * @property defaultMargins
62205      * @type String
62206      * The margins to apply by default to each field in the composite
62207      */
62208     defaultMargins: '0 5 0 0',
62209
62210     /**
62211      * @property skipLastItemMargin
62212      * @type Boolean
62213      * If true, the defaultMargins are not applied to the last item in the composite field set (defaults to true)
62214      */
62215     skipLastItemMargin: true,
62216
62217     /**
62218      * @property isComposite
62219      * @type Boolean
62220      * Signifies that this is a Composite field
62221      */
62222     isComposite: true,
62223
62224     /**
62225      * @property combineErrors
62226      * @type Boolean
62227      * True to combine errors from the individual fields into a single error message at the CompositeField level (defaults to true)
62228      */
62229     combineErrors: true,
62230
62231     //inherit docs
62232     //Builds the composite field label
62233     initComponent: function() {
62234         var labels = [],
62235             items  = this.items,
62236             item;
62237
62238         for (var i=0, j = items.length; i < j; i++) {
62239             item = items[i];
62240
62241             labels.push(item.fieldLabel);
62242
62243             //apply any defaults
62244             Ext.apply(item, this.defaults);
62245
62246             //apply default margins to each item except the last
62247             if (!(i == j - 1 && this.skipLastItemMargin)) {
62248                 Ext.applyIf(item, {margins: this.defaultMargins});
62249             }
62250         }
62251
62252         this.fieldLabel = this.fieldLabel || this.buildLabel(labels);
62253
62254         /**
62255          * @property fieldErrors
62256          * @type Ext.util.MixedCollection
62257          * MixedCollection of current errors on the Composite's subfields. This is used internally to track when
62258          * to show and hide error messages at the Composite level. Listeners are attached to the MixedCollection's
62259          * add, remove and replace events to update the error icon in the UI as errors are added or removed.
62260          */
62261         this.fieldErrors = new Ext.util.MixedCollection(true, function(item) {
62262             return item.field;
62263         });
62264
62265         this.fieldErrors.on({
62266             scope  : this,
62267             add    : this.updateInvalidMark,
62268             remove : this.updateInvalidMark,
62269             replace: this.updateInvalidMark
62270         });
62271
62272         Ext.form.CompositeField.superclass.initComponent.apply(this, arguments);
62273     },
62274
62275     /**
62276      * @private
62277      * Creates an internal container using hbox and renders the fields to it
62278      */
62279     onRender: function(ct, position) {
62280         if (!this.el) {
62281             /**
62282              * @property innerCt
62283              * @type Ext.Container
62284              * A container configured with hbox layout which is responsible for laying out the subfields
62285              */
62286             var innerCt = this.innerCt = new Ext.Container({
62287                 layout  : 'hbox',
62288                 renderTo: ct,
62289                 items   : this.items,
62290                 cls     : 'x-form-composite',
62291                 defaultMargins: '0 3 0 0'
62292             });
62293
62294             this.el = innerCt.getEl();
62295
62296             var fields = innerCt.findBy(function(c) {
62297                 return c.isFormField;
62298             }, this);
62299
62300             /**
62301              * @property items
62302              * @type Ext.util.MixedCollection
62303              * Internal collection of all of the subfields in this Composite
62304              */
62305             this.items = new Ext.util.MixedCollection();
62306             this.items.addAll(fields);
62307
62308             //if we're combining subfield errors into a single message, override the markInvalid and clearInvalid
62309             //methods of each subfield and show them at the Composite level instead
62310             if (this.combineErrors) {
62311                 this.eachItem(function(field) {
62312                     Ext.apply(field, {
62313                         markInvalid : this.onFieldMarkInvalid.createDelegate(this, [field], 0),
62314                         clearInvalid: this.onFieldClearInvalid.createDelegate(this, [field], 0)
62315                     });
62316                 });
62317             }
62318
62319             //set the label 'for' to the first item
62320             var l = this.el.parent().parent().child('label', true);
62321             if (l) {
62322                 l.setAttribute('for', this.items.items[0].id);
62323             }
62324         }
62325
62326         Ext.form.CompositeField.superclass.onRender.apply(this, arguments);
62327     },
62328
62329     /**
62330      * Called if combineErrors is true and a subfield's markInvalid method is called.
62331      * By default this just adds the subfield's error to the internal fieldErrors MixedCollection
62332      * @param {Ext.form.Field} field The field that was marked invalid
62333      * @param {String} message The error message
62334      */
62335     onFieldMarkInvalid: function(field, message) {
62336         var name  = field.getName(),
62337             error = {field: name, error: message};
62338
62339         this.fieldErrors.replace(name, error);
62340
62341         field.el.addClass(field.invalidClass);
62342     },
62343
62344     /**
62345      * Called if combineErrors is true and a subfield's clearInvalid method is called.
62346      * By default this just updates the internal fieldErrors MixedCollection.
62347      * @param {Ext.form.Field} field The field that was marked invalid
62348      */
62349     onFieldClearInvalid: function(field) {
62350         this.fieldErrors.removeKey(field.getName());
62351
62352         field.el.removeClass(field.invalidClass);
62353     },
62354
62355     /**
62356      * @private
62357      * Called after a subfield is marked valid or invalid, this checks to see if any of the subfields are
62358      * currently invalid. If any subfields are invalid it builds a combined error message marks the composite
62359      * invalid, otherwise clearInvalid is called
62360      */
62361     updateInvalidMark: function() {
62362         var ieStrict = Ext.isIE6 && Ext.isStrict;
62363
62364         if (this.fieldErrors.length == 0) {
62365             this.clearInvalid();
62366
62367             //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
62368             if (ieStrict) {
62369                 this.clearInvalid.defer(50, this);
62370             }
62371         } else {
62372             var message = this.buildCombinedErrorMessage(this.fieldErrors.items);
62373
62374             this.sortErrors();
62375             this.markInvalid(message);
62376
62377             //IE6 in strict mode has a layout bug when using 'under' as the error message target. This fixes it
62378             if (ieStrict) {
62379                 this.markInvalid(message);
62380             }
62381         }
62382     },
62383
62384     /**
62385      * Performs validation checks on each subfield and returns false if any of them fail validation.
62386      * @return {Boolean} False if any subfield failed validation
62387      */
62388     validateValue: function() {
62389         var valid = true;
62390
62391         this.eachItem(function(field) {
62392             if (!field.isValid()) valid = false;
62393         });
62394
62395         return valid;
62396     },
62397
62398     /**
62399      * Takes an object containing error messages for contained fields, returning a combined error
62400      * string (defaults to just placing each item on a new line). This can be overridden to provide
62401      * custom combined error message handling.
62402      * @param {Array} errors Array of errors in format: [{field: 'title', error: 'some error'}]
62403      * @return {String} The combined error message
62404      */
62405     buildCombinedErrorMessage: function(errors) {
62406         var combined = [],
62407             error;
62408
62409         for (var i = 0, j = errors.length; i < j; i++) {
62410             error = errors[i];
62411
62412             combined.push(String.format("{0}: {1}", error.field, error.error));
62413         }
62414
62415         return combined.join("<br />");
62416     },
62417
62418     /**
62419      * Sorts the internal fieldErrors MixedCollection by the order in which the fields are defined.
62420      * This is called before displaying errors to ensure that the errors are presented in the expected order.
62421      * This function can be overridden to provide a custom sorting order if needed.
62422      */
62423     sortErrors: function() {
62424         var fields = this.items;
62425
62426         this.fieldErrors.sort("ASC", function(a, b) {
62427             var findByName = function(key) {
62428                 return function(field) {
62429                     return field.getName() == key;
62430                 };
62431             };
62432
62433             var aIndex = fields.findIndexBy(findByName(a.field)),
62434                 bIndex = fields.findIndexBy(findByName(b.field));
62435
62436             return aIndex < bIndex ? -1 : 1;
62437         });
62438     },
62439
62440     /**
62441      * Resets each field in the composite to their previous value
62442      */
62443     reset: function() {
62444         this.eachItem(function(item) {
62445             item.reset();
62446         });
62447
62448         // Defer the clearInvalid so if BaseForm's collection is being iterated it will be called AFTER it is complete.
62449         // Important because reset is being called on both the group and the individual items.
62450         (function() {
62451             this.clearInvalid();
62452         }).defer(50, this);
62453     },
62454     
62455     /**
62456      * Calls clearInvalid on all child fields. This is a convenience function and should not often need to be called
62457      * as fields usually take care of clearing themselves
62458      */
62459     clearInvalidChildren: function() {
62460         this.eachItem(function(item) {
62461             item.clearInvalid();
62462         });
62463     },
62464
62465     /**
62466      * Builds a label string from an array of subfield labels.
62467      * By default this just joins the labels together with a comma
62468      * @param {Array} segments Array of each of the labels in the composite field's subfields
62469      * @return {String} The built label
62470      */
62471     buildLabel: function(segments) {
62472         return segments.join(", ");
62473     },
62474
62475     /**
62476      * Checks each field in the composite and returns true if any is dirty
62477      * @return {Boolean} True if any field is dirty
62478      */
62479     isDirty: function(){
62480         //override the behaviour to check sub items.
62481         if (this.disabled || !this.rendered) {
62482             return false;
62483         }
62484
62485         var dirty = false;
62486         this.eachItem(function(item){
62487             if(item.isDirty()){
62488                 dirty = true;
62489                 return false;
62490             }
62491         });
62492         return dirty;
62493     },
62494
62495     /**
62496      * @private
62497      * Convenience function which passes the given function to every item in the composite
62498      * @param {Function} fn The function to call
62499      * @param {Object} scope Optional scope object
62500      */
62501     eachItem: function(fn, scope) {
62502         if(this.items && this.items.each){
62503             this.items.each(fn, scope || this);
62504         }
62505     },
62506
62507     /**
62508      * @private
62509      * Passes the resize call through to the inner panel
62510      */
62511     onResize: function(adjWidth, adjHeight, rawWidth, rawHeight) {
62512         var innerCt = this.innerCt;
62513
62514         if (this.rendered && innerCt.rendered) {
62515             innerCt.setSize(adjWidth, adjHeight);
62516         }
62517
62518         Ext.form.CompositeField.superclass.onResize.apply(this, arguments);
62519     },
62520
62521     /**
62522      * @private
62523      * Forces the internal container to be laid out again
62524      */
62525     doLayout: function(shallow, force) {
62526         if (this.rendered) {
62527             var innerCt = this.innerCt;
62528
62529             innerCt.forceLayout = this.ownerCt.forceLayout;
62530             innerCt.doLayout(shallow, force);
62531         }
62532     },
62533
62534     /**
62535      * @private
62536      */
62537     beforeDestroy: function(){
62538         Ext.destroy(this.innerCt);
62539
62540         Ext.form.CompositeField.superclass.beforeDestroy.call(this);
62541     },
62542
62543     //override the behaviour to check sub items.
62544     setReadOnly : function(readOnly) {
62545         readOnly = readOnly || true;
62546
62547         if(this.rendered){
62548             this.eachItem(function(item){
62549                 item.setReadOnly(readOnly);
62550             });
62551         }
62552         this.readOnly = readOnly;
62553     },
62554
62555     onShow : function() {
62556         Ext.form.CompositeField.superclass.onShow.call(this);
62557         this.doLayout();
62558     },
62559
62560     //override the behaviour to check sub items.
62561     onDisable : function(){
62562         this.eachItem(function(item){
62563             item.disable();
62564         });
62565     },
62566
62567     //override the behaviour to check sub items.
62568     onEnable : function(){
62569         this.eachItem(function(item){
62570             item.enable();
62571         });
62572     }
62573 });
62574
62575 Ext.reg('compositefield', Ext.form.CompositeField);
62576 /**
62577  * @class Ext.form.Radio
62578  * @extends Ext.form.Checkbox
62579  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
62580  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
62581  * @constructor
62582  * Creates a new Radio
62583  * @param {Object} config Configuration options
62584  * @xtype radio
62585  */
62586 Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
62587     inputType: 'radio',
62588
62589     /**
62590      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
62591      * @method
62592      */
62593     markInvalid : Ext.emptyFn,
62594     /**
62595      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
62596      * @method
62597      */
62598     clearInvalid : Ext.emptyFn,
62599
62600     /**
62601      * If this radio is part of a group, it will return the selected value
62602      * @return {String}
62603      */
62604     getGroupValue : function(){
62605         var p = this.el.up('form') || Ext.getBody();
62606         var c = p.child('input[name='+this.el.dom.name+']:checked', true);
62607         return c ? c.value : null;
62608     },
62609
62610     // private
62611     onClick : function(){
62612         if(this.el.dom.checked != this.checked){
62613                         var els = this.getCheckEl().select('input[name=' + this.el.dom.name + ']');
62614                         els.each(function(el){
62615                                 if(el.dom.id == this.id){
62616                                         this.setValue(true);
62617                                 }else{
62618                                         Ext.getCmp(el.dom.id).setValue(false);
62619                                 }
62620                         }, this);
62621                 }
62622     },
62623
62624     /**
62625      * Sets either the checked/unchecked status of this Radio, or, if a string value
62626      * is passed, checks a sibling Radio of the same name whose value is the value specified.
62627      * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
62628      * @return {Ext.form.Field} this
62629      */
62630     setValue : function(v){
62631         if (typeof v == 'boolean') {
62632             Ext.form.Radio.superclass.setValue.call(this, v);
62633         } else if (this.rendered) {
62634             var r = this.getCheckEl().child('input[name=' + this.el.dom.name + '][value=' + v + ']', true);
62635             if(r){
62636                 Ext.getCmp(r.id).setValue(true);
62637             }
62638         }
62639         return this;
62640     },
62641
62642     // private
62643     getCheckEl: function(){
62644         if(this.inGroup){
62645             return this.el.up('.x-form-radio-group')
62646         }
62647         return this.el.up('form') || Ext.getBody();
62648     }
62649 });
62650 Ext.reg('radio', Ext.form.Radio);
62651 /**
62652  * @class Ext.form.RadioGroup
62653  * @extends Ext.form.CheckboxGroup
62654  * A grouping container for {@link Ext.form.Radio} controls.
62655  * @constructor
62656  * Creates a new RadioGroup
62657  * @param {Object} config Configuration options
62658  * @xtype radiogroup
62659  */
62660 Ext.form.RadioGroup = Ext.extend(Ext.form.CheckboxGroup, {
62661     /**
62662      * @cfg {Array} items An Array of {@link Ext.form.Radio Radio}s or Radio config objects
62663      * to arrange in the group.
62664      */
62665     /**
62666      * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
62667      * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
62668      * be used as the error text.
62669      */
62670     allowBlank : true,
62671     /**
62672      * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
62673      * (defaults to 'You must select one item in this group')
62674      */
62675     blankText : 'You must select one item in this group',
62676     
62677     // private
62678     defaultType : 'radio',
62679     
62680     // private
62681     groupCls : 'x-form-radio-group',
62682     
62683     /**
62684      * @event change
62685      * Fires when the state of a child radio changes.
62686      * @param {Ext.form.RadioGroup} this
62687      * @param {Ext.form.Radio} checked The checked radio
62688      */
62689     
62690     /**
62691      * Gets the selected {@link Ext.form.Radio} in the group, if it exists.
62692      * @return {Ext.form.Radio} The selected radio.
62693      */
62694     getValue : function(){
62695         var out = null;
62696         this.eachItem(function(item){
62697             if(item.checked){
62698                 out = item;
62699                 return false;
62700             }
62701         });
62702         return out;
62703     },
62704     
62705     /**
62706      * Sets the checked radio in the group.
62707      * @param {String/Ext.form.Radio} id The radio to check.
62708      * @param {Boolean} value The value to set the radio.
62709      * @return {Ext.form.RadioGroup} this
62710      */
62711     onSetValue : function(id, value){
62712         if(arguments.length > 1){
62713             var f = this.getBox(id);
62714             if(f){
62715                 f.setValue(value);
62716                 if(f.checked){
62717                     this.eachItem(function(item){
62718                         if (item !== f){
62719                             item.setValue(false);
62720                         }
62721                     });
62722                 }
62723             }
62724         }else{
62725             this.setValueForItem(id);
62726         }
62727     },
62728     
62729     setValueForItem : function(val){
62730         val = String(val).split(',')[0];
62731         this.eachItem(function(item){
62732             item.setValue(val == item.inputValue);
62733         });
62734     },
62735     
62736     // private
62737     fireChecked : function(){
62738         if(!this.checkTask){
62739             this.checkTask = new Ext.util.DelayedTask(this.bufferChecked, this);
62740         }
62741         this.checkTask.delay(10);
62742     },
62743     
62744     // private
62745     bufferChecked : function(){
62746         var out = null;
62747         this.eachItem(function(item){
62748             if(item.checked){
62749                 out = item;
62750                 return false;
62751             }
62752         });
62753         this.fireEvent('change', this, out);
62754     },
62755     
62756     onDestroy : function(){
62757         if(this.checkTask){
62758             this.checkTask.cancel();
62759             this.checkTask = null;
62760         }
62761         Ext.form.RadioGroup.superclass.onDestroy.call(this);
62762     }
62763
62764 });
62765
62766 Ext.reg('radiogroup', Ext.form.RadioGroup);
62767 /**
62768  * @class Ext.form.Hidden
62769  * @extends Ext.form.Field
62770  * A basic hidden field for storing hidden values in forms that need to be passed in the form submit.
62771  * @constructor
62772  * Create a new Hidden field.
62773  * @param {Object} config Configuration options
62774  * @xtype hidden
62775  */
62776 Ext.form.Hidden = Ext.extend(Ext.form.Field, {
62777     // private
62778     inputType : 'hidden',
62779
62780     // private
62781     onRender : function(){
62782         Ext.form.Hidden.superclass.onRender.apply(this, arguments);
62783     },
62784
62785     // private
62786     initEvents : function(){
62787         this.originalValue = this.getValue();
62788     },
62789
62790     // These are all private overrides
62791     setSize : Ext.emptyFn,
62792     setWidth : Ext.emptyFn,
62793     setHeight : Ext.emptyFn,
62794     setPosition : Ext.emptyFn,
62795     setPagePosition : Ext.emptyFn,
62796     markInvalid : Ext.emptyFn,
62797     clearInvalid : Ext.emptyFn
62798 });
62799 Ext.reg('hidden', Ext.form.Hidden);/**
62800  * @class Ext.form.BasicForm
62801  * @extends Ext.util.Observable
62802  * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
62803  * input field management, validation, submission, and form loading services.</p>
62804  * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
62805  * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
62806  * <p><b><u>File Uploads</u></b></p>
62807  * <p>{@link #fileUpload File uploads} are not performed using Ajax submission, that
62808  * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
62809  * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
62810  * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
62811  * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
62812  * but removed after the return data has been gathered.</p>
62813  * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
62814  * server is using JSON to send the return object, then the
62815  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
62816  * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
62817  * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
62818  * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
62819  * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
62820  * is created containing a <tt>responseText</tt> property in order to conform to the
62821  * requirements of event handlers and callbacks.</p>
62822  * <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>
62823  * and some server technologies (notably JEE) may require some custom processing in order to
62824  * retrieve parameter names and parameter values from the packet content.</p>
62825  * @constructor
62826  * @param {Mixed} el The form element or its id
62827  * @param {Object} config Configuration options
62828  */
62829 Ext.form.BasicForm = Ext.extend(Ext.util.Observable, {
62830
62831     constructor: function(el, config){
62832         Ext.apply(this, config);
62833         if(Ext.isString(this.paramOrder)){
62834             this.paramOrder = this.paramOrder.split(/[\s,|]/);
62835         }
62836         /**
62837          * A {@link Ext.util.MixedCollection MixedCollection} containing all the Ext.form.Fields in this form.
62838          * @type MixedCollection
62839          * @property items
62840          */
62841         this.items = new Ext.util.MixedCollection(false, function(o){
62842             return o.getItemId();
62843         });
62844         this.addEvents(
62845             /**
62846              * @event beforeaction
62847              * Fires before any action is performed. Return false to cancel the action.
62848              * @param {Form} this
62849              * @param {Action} action The {@link Ext.form.Action} to be performed
62850              */
62851             'beforeaction',
62852             /**
62853              * @event actionfailed
62854              * Fires when an action fails.
62855              * @param {Form} this
62856              * @param {Action} action The {@link Ext.form.Action} that failed
62857              */
62858             'actionfailed',
62859             /**
62860              * @event actioncomplete
62861              * Fires when an action is completed.
62862              * @param {Form} this
62863              * @param {Action} action The {@link Ext.form.Action} that completed
62864              */
62865             'actioncomplete'
62866         );
62867
62868         if(el){
62869             this.initEl(el);
62870         }
62871         Ext.form.BasicForm.superclass.constructor.call(this);
62872     },
62873
62874     /**
62875      * @cfg {String} method
62876      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
62877      */
62878     /**
62879      * @cfg {DataReader} reader
62880      * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read
62881      * data when executing 'load' actions. This is optional as there is built-in
62882      * support for processing JSON.  For additional information on using an XMLReader
62883      * see the example provided in examples/form/xml-form.html.
62884      */
62885     /**
62886      * @cfg {DataReader} errorReader
62887      * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to
62888      * read field error messages returned from 'submit' actions. This is optional
62889      * as there is built-in support for processing JSON.</p>
62890      * <p>The Records which provide messages for the invalid Fields must use the
62891      * Field name (or id) as the Record ID, and must contain a field called 'msg'
62892      * which contains the error message.</p>
62893      * <p>The errorReader does not have to be a full-blown implementation of a
62894      * DataReader. It simply needs to implement a <tt>read(xhr)</tt> function
62895      * which returns an Array of Records in an object with the following
62896      * structure:</p><pre><code>
62897 {
62898     records: recordArray
62899 }
62900 </code></pre>
62901      */
62902     /**
62903      * @cfg {String} url
62904      * The URL to use for form actions if one isn't supplied in the
62905      * <code>{@link #doAction doAction} options</code>.
62906      */
62907     /**
62908      * @cfg {Boolean} fileUpload
62909      * Set to true if this form is a file upload.
62910      * <p>File uploads are not performed using normal 'Ajax' techniques, that is they are <b>not</b>
62911      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
62912      * DOM <tt>&lt;form></tt> element temporarily modified to have its
62913      * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
62914      * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
62915      * but removed after the return data has been gathered.</p>
62916      * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
62917      * server is using JSON to send the return object, then the
62918      * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
62919      * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
62920      * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
62921      * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
62922      * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
62923      * is created containing a <tt>responseText</tt> property in order to conform to the
62924      * requirements of event handlers and callbacks.</p>
62925      * <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>
62926      * and some server technologies (notably JEE) may require some custom processing in order to
62927      * retrieve parameter names and parameter values from the packet content.</p>
62928      */
62929     /**
62930      * @cfg {Object} baseParams
62931      * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
62932      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
62933      */
62934     /**
62935      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
62936      */
62937     timeout: 30,
62938
62939     /**
62940      * @cfg {Object} api (Optional) If specified load and submit actions will be handled
62941      * with {@link Ext.form.Action.DirectLoad} and {@link Ext.form.Action.DirectSubmit}.
62942      * Methods which have been imported by Ext.Direct can be specified here to load and submit
62943      * forms.
62944      * Such as the following:<pre><code>
62945 api: {
62946     load: App.ss.MyProfile.load,
62947     submit: App.ss.MyProfile.submit
62948 }
62949 </code></pre>
62950      * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
62951      * to customize how the load method is invoked.
62952      * Submit actions will always use a standard form submit. The formHandler configuration must
62953      * be set on the associated server-side method which has been imported by Ext.Direct</p>
62954      */
62955
62956     /**
62957      * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
62958      * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
62959      * <code>load</code> configuration.</p>
62960      * <br><p>Specify the params in the order in which they must be executed on the
62961      * server-side as either (1) an Array of String values, or (2) a String of params
62962      * delimited by either whitespace, comma, or pipe. For example,
62963      * any of the following would be acceptable:</p><pre><code>
62964 paramOrder: ['param1','param2','param3']
62965 paramOrder: 'param1 param2 param3'
62966 paramOrder: 'param1,param2,param3'
62967 paramOrder: 'param1|param2|param'
62968      </code></pre>
62969      */
62970     paramOrder: undefined,
62971
62972     /**
62973      * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
62974      * <code>load</code> configuration. Send parameters as a collection of named
62975      * arguments (defaults to <tt>false</tt>). Providing a
62976      * <tt>{@link #paramOrder}</tt> nullifies this configuration.
62977      */
62978     paramsAsHash: false,
62979
62980     /**
62981      * @cfg {String} waitTitle
62982      * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
62983      */
62984     waitTitle: 'Please Wait...',
62985
62986     // private
62987     activeAction : null,
62988
62989     /**
62990      * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
62991      * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
62992      */
62993     trackResetOnLoad : false,
62994
62995     /**
62996      * @cfg {Boolean} standardSubmit
62997      * <p>If set to <tt>true</tt>, standard HTML form submits are used instead
62998      * of XHR (Ajax) style form submissions. Defaults to <tt>false</tt>.</p>
62999      * <br><p><b>Note:</b> When using <code>standardSubmit</code>, the
63000      * <code>options</code> to <code>{@link #submit}</code> are ignored because
63001      * Ext's Ajax infrastracture is bypassed. To pass extra parameters (e.g.
63002      * <code>baseParams</code> and <code>params</code>), utilize hidden fields
63003      * to submit extra data, for example:</p>
63004      * <pre><code>
63005 new Ext.FormPanel({
63006     standardSubmit: true,
63007     baseParams: {
63008         foo: 'bar'
63009     },
63010     {@link url}: 'myProcess.php',
63011     items: [{
63012         xtype: 'textfield',
63013         name: 'userName'
63014     }],
63015     buttons: [{
63016         text: 'Save',
63017         handler: function(){
63018             var fp = this.ownerCt.ownerCt,
63019                 form = fp.getForm();
63020             if (form.isValid()) {
63021                 // check if there are baseParams and if
63022                 // hiddent items have been added already
63023                 if (fp.baseParams && !fp.paramsAdded) {
63024                     // add hidden items for all baseParams
63025                     for (i in fp.baseParams) {
63026                         fp.add({
63027                             xtype: 'hidden',
63028                             name: i,
63029                             value: fp.baseParams[i]
63030                         });
63031                     }
63032                     fp.doLayout();
63033                     // set a custom flag to prevent re-adding
63034                     fp.paramsAdded = true;
63035                 }
63036                 form.{@link #submit}();
63037             }
63038         }
63039     }]
63040 });
63041      * </code></pre>
63042      */
63043     /**
63044      * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
63045      * element by passing it or its id or mask the form itself by passing in true.
63046      * @type Mixed
63047      * @property waitMsgTarget
63048      */
63049
63050     // private
63051     initEl : function(el){
63052         this.el = Ext.get(el);
63053         this.id = this.el.id || Ext.id();
63054         if(!this.standardSubmit){
63055             this.el.on('submit', this.onSubmit, this);
63056         }
63057         this.el.addClass('x-form');
63058     },
63059
63060     /**
63061      * Get the HTML form Element
63062      * @return Ext.Element
63063      */
63064     getEl: function(){
63065         return this.el;
63066     },
63067
63068     // private
63069     onSubmit : function(e){
63070         e.stopEvent();
63071     },
63072
63073     /**
63074      * Destroys this object.
63075      * @private
63076      * @param {Boolean} bound true if the object is bound to a form panel. If this is the case
63077      * the FormPanel will take care of destroying certain things, so we're just doubling up.
63078      */
63079     destroy: function(bound){
63080         if(bound !== true){
63081             this.items.each(function(f){
63082                 Ext.destroy(f);
63083             });
63084             Ext.destroy(this.el);
63085         }
63086         this.items.clear();
63087         this.purgeListeners();
63088     },
63089
63090     /**
63091      * Returns true if client-side validation on the form is successful.
63092      * @return Boolean
63093      */
63094     isValid : function(){
63095         var valid = true;
63096         this.items.each(function(f){
63097            if(!f.validate()){
63098                valid = false;
63099            }
63100         });
63101         return valid;
63102     },
63103
63104     /**
63105      * <p>Returns true if any fields in this form have changed from their original values.</p>
63106      * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
63107      * Fields' <i>original values</i> are updated when the values are loaded by {@link #setValues}
63108      * or {@link #loadRecord}.</p>
63109      * @return Boolean
63110      */
63111     isDirty : function(){
63112         var dirty = false;
63113         this.items.each(function(f){
63114            if(f.isDirty()){
63115                dirty = true;
63116                return false;
63117            }
63118         });
63119         return dirty;
63120     },
63121
63122     /**
63123      * Performs a predefined action ({@link Ext.form.Action.Submit} or
63124      * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
63125      * to perform application-specific processing.
63126      * @param {String/Object} actionName The name of the predefined action type,
63127      * or instance of {@link Ext.form.Action} to perform.
63128      * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
63129      * All of the config options listed below are supported by both the
63130      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
63131      * actions unless otherwise noted (custom actions could also accept
63132      * other config options):<ul>
63133      *
63134      * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
63135      * to the form's {@link #url}.)</div></li>
63136      *
63137      * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
63138      * to the form's method, or POST if not defined)</div></li>
63139      *
63140      * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
63141      * (defaults to the form's baseParams, or none if not defined)</p>
63142      * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
63143      *
63144      * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action
63145      * (defaults to the form's default headers)</div></li>
63146      *
63147      * <li><b>success</b> : Function<div class="sub-desc">The callback that will
63148      * be invoked after a successful response (see top of
63149      * {@link Ext.form.Action.Submit submit} and {@link Ext.form.Action.Load load}
63150      * for a description of what constitutes a successful response).
63151      * The function is passed the following parameters:<ul>
63152      * <li><tt>form</tt> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
63153      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
63154      * <div class="sub-desc">The action object contains these properties of interest:<ul>
63155      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
63156      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
63157      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
63158      * </ul></div></li></ul></div></li>
63159      *
63160      * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
63161      * failed transaction attempt. The function is passed the following parameters:<ul>
63162      * <li><tt>form</tt> : The {@link Ext.form.BasicForm} that requested the action.</li>
63163      * <li><tt>action</tt> : The {@link Ext.form.Action Action} object which performed the operation.
63164      * <div class="sub-desc">The action object contains these properties of interest:<ul>
63165      * <li><tt>{@link Ext.form.Action#failureType failureType}</tt></li>
63166      * <li><tt>{@link Ext.form.Action#response response}</tt></li>
63167      * <li><tt>{@link Ext.form.Action#result result}</tt> : interrogate for custom postprocessing</li>
63168      * <li><tt>{@link Ext.form.Action#type type}</tt></li>
63169      * </ul></div></li></ul></div></li>
63170      *
63171      * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
63172      * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
63173      *
63174      * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
63175      * Determines whether a Form's fields are validated in a final call to
63176      * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
63177      * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
63178      *
63179      * @return {BasicForm} this
63180      */
63181     doAction : function(action, options){
63182         if(Ext.isString(action)){
63183             action = new Ext.form.Action.ACTION_TYPES[action](this, options);
63184         }
63185         if(this.fireEvent('beforeaction', this, action) !== false){
63186             this.beforeAction(action);
63187             action.run.defer(100, action);
63188         }
63189         return this;
63190     },
63191
63192     /**
63193      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Submit submit action}.
63194      * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
63195      * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
63196      * <p>The following code:</p><pre><code>
63197 myFormPanel.getForm().submit({
63198     clientValidation: true,
63199     url: 'updateConsignment.php',
63200     params: {
63201         newStatus: 'delivered'
63202     },
63203     success: function(form, action) {
63204        Ext.Msg.alert('Success', action.result.msg);
63205     },
63206     failure: function(form, action) {
63207         switch (action.failureType) {
63208             case Ext.form.Action.CLIENT_INVALID:
63209                 Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
63210                 break;
63211             case Ext.form.Action.CONNECT_FAILURE:
63212                 Ext.Msg.alert('Failure', 'Ajax communication failed');
63213                 break;
63214             case Ext.form.Action.SERVER_INVALID:
63215                Ext.Msg.alert('Failure', action.result.msg);
63216        }
63217     }
63218 });
63219 </code></pre>
63220      * would process the following server response for a successful submission:<pre><code>
63221 {
63222     "success":true, // note this is Boolean, not string
63223     "msg":"Consignment updated"
63224 }
63225 </code></pre>
63226      * and the following server response for a failed submission:<pre><code>
63227 {
63228     "success":false, // note this is Boolean, not string
63229     "msg":"You do not have permission to perform this operation"
63230 }
63231 </code></pre>
63232      * @return {BasicForm} this
63233      */
63234     submit : function(options){
63235         options = options || {};
63236         if(this.standardSubmit){
63237             var v = options.clientValidation === false || this.isValid();
63238             if(v){
63239                 var el = this.el.dom;
63240                 if(this.url && Ext.isEmpty(el.action)){
63241                     el.action = this.url;
63242                 }
63243                 el.submit();
63244             }
63245             return v;
63246         }
63247         var submitAction = String.format('{0}submit', this.api ? 'direct' : '');
63248         this.doAction(submitAction, options);
63249         return this;
63250     },
63251
63252     /**
63253      * Shortcut to {@link #doAction do} a {@link Ext.form.Action.Load load action}.
63254      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
63255      * @return {BasicForm} this
63256      */
63257     load : function(options){
63258         var loadAction = String.format('{0}load', this.api ? 'direct' : '');
63259         this.doAction(loadAction, options);
63260         return this;
63261     },
63262
63263     /**
63264      * Persists the values in this form into the passed {@link Ext.data.Record} object in a beginEdit/endEdit block.
63265      * @param {Record} record The record to edit
63266      * @return {BasicForm} this
63267      */
63268     updateRecord : function(record){
63269         record.beginEdit();
63270         var fs = record.fields;
63271         fs.each(function(f){
63272             var field = this.findField(f.name);
63273             if(field){
63274                 record.set(f.name, field.getValue());
63275             }
63276         }, this);
63277         record.endEdit();
63278         return this;
63279     },
63280
63281     /**
63282      * Loads an {@link Ext.data.Record} into this form by calling {@link #setValues} with the
63283      * {@link Ext.data.Record#data record data}.
63284      * See also {@link #trackResetOnLoad}.
63285      * @param {Record} record The record to load
63286      * @return {BasicForm} this
63287      */
63288     loadRecord : function(record){
63289         this.setValues(record.data);
63290         return this;
63291     },
63292
63293     // private
63294     beforeAction : function(action){
63295         // Call HtmlEditor's syncValue before actions
63296         this.items.each(function(f){
63297             if(f.isFormField && f.syncValue){
63298                 f.syncValue();
63299             }
63300         });
63301         var o = action.options;
63302         if(o.waitMsg){
63303             if(this.waitMsgTarget === true){
63304                 this.el.mask(o.waitMsg, 'x-mask-loading');
63305             }else if(this.waitMsgTarget){
63306                 this.waitMsgTarget = Ext.get(this.waitMsgTarget);
63307                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
63308             }else{
63309                 Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
63310             }
63311         }
63312     },
63313
63314     // private
63315     afterAction : function(action, success){
63316         this.activeAction = null;
63317         var o = action.options;
63318         if(o.waitMsg){
63319             if(this.waitMsgTarget === true){
63320                 this.el.unmask();
63321             }else if(this.waitMsgTarget){
63322                 this.waitMsgTarget.unmask();
63323             }else{
63324                 Ext.MessageBox.updateProgress(1);
63325                 Ext.MessageBox.hide();
63326             }
63327         }
63328         if(success){
63329             if(o.reset){
63330                 this.reset();
63331             }
63332             Ext.callback(o.success, o.scope, [this, action]);
63333             this.fireEvent('actioncomplete', this, action);
63334         }else{
63335             Ext.callback(o.failure, o.scope, [this, action]);
63336             this.fireEvent('actionfailed', this, action);
63337         }
63338     },
63339
63340     /**
63341      * Find a {@link Ext.form.Field} in this form.
63342      * @param {String} id The value to search for (specify either a {@link Ext.Component#id id},
63343      * {@link Ext.grid.Column#dataIndex dataIndex}, {@link Ext.form.Field#getName name or hiddenName}).
63344      * @return Field
63345      */
63346     findField : function(id) {
63347         var field = this.items.get(id);
63348
63349         if (!Ext.isObject(field)) {
63350             //searches for the field corresponding to the given id. Used recursively for composite fields
63351             var findMatchingField = function(f) {
63352                 if (f.isFormField) {
63353                     if (f.dataIndex == id || f.id == id || f.getName() == id) {
63354                         field = f;
63355                         return false;
63356                     } else if (f.isComposite && f.rendered) {
63357                         return f.items.each(findMatchingField);
63358                     }
63359                 }
63360             };
63361
63362             this.items.each(findMatchingField);
63363         }
63364         return field || null;
63365     },
63366
63367
63368     /**
63369      * Mark fields in this form invalid in bulk.
63370      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
63371      * @return {BasicForm} this
63372      */
63373     markInvalid : function(errors){
63374         if (Ext.isArray(errors)) {
63375             for(var i = 0, len = errors.length; i < len; i++){
63376                 var fieldError = errors[i];
63377                 var f = this.findField(fieldError.id);
63378                 if(f){
63379                     f.markInvalid(fieldError.msg);
63380                 }
63381             }
63382         } else {
63383             var field, id;
63384             for(id in errors){
63385                 if(!Ext.isFunction(errors[id]) && (field = this.findField(id))){
63386                     field.markInvalid(errors[id]);
63387                 }
63388             }
63389         }
63390
63391         return this;
63392     },
63393
63394     /**
63395      * Set values for fields in this form in bulk.
63396      * @param {Array/Object} values Either an array in the form:<pre><code>
63397 [{id:'clientName', value:'Fred. Olsen Lines'},
63398  {id:'portOfLoading', value:'FXT'},
63399  {id:'portOfDischarge', value:'OSL'} ]</code></pre>
63400      * or an object hash of the form:<pre><code>
63401 {
63402     clientName: 'Fred. Olsen Lines',
63403     portOfLoading: 'FXT',
63404     portOfDischarge: 'OSL'
63405 }</code></pre>
63406      * @return {BasicForm} this
63407      */
63408     setValues : function(values){
63409         if(Ext.isArray(values)){ // array of objects
63410             for(var i = 0, len = values.length; i < len; i++){
63411                 var v = values[i];
63412                 var f = this.findField(v.id);
63413                 if(f){
63414                     f.setValue(v.value);
63415                     if(this.trackResetOnLoad){
63416                         f.originalValue = f.getValue();
63417                     }
63418                 }
63419             }
63420         }else{ // object hash
63421             var field, id;
63422             for(id in values){
63423                 if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
63424                     field.setValue(values[id]);
63425                     if(this.trackResetOnLoad){
63426                         field.originalValue = field.getValue();
63427                     }
63428                 }
63429             }
63430         }
63431         return this;
63432     },
63433
63434     /**
63435      * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
63436      * If multiple fields exist with the same name they are returned as an array.</p>
63437      * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
63438      * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the
63439      * value can potentially be the emptyText of a field.</p>
63440      * @param {Boolean} asString (optional) Pass true to return the values as a string. (defaults to false, returning an Object)
63441      * @return {String/Object}
63442      */
63443     getValues : function(asString){
63444         var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
63445         if(asString === true){
63446             return fs;
63447         }
63448         return Ext.urlDecode(fs);
63449     },
63450
63451     /**
63452      * Retrieves the fields in the form as a set of key/value pairs, using the {@link Ext.form.Field#getValue getValue()} method.
63453      * If multiple fields exist with the same name they are returned as an array.
63454      * @param {Boolean} dirtyOnly (optional) True to return only fields that are dirty.
63455      * @return {Object} The values in the form
63456      */
63457     getFieldValues : function(dirtyOnly){
63458         var o = {},
63459             n,
63460             key,
63461             val;
63462         this.items.each(function(f) {
63463             if (dirtyOnly !== true || f.isDirty()) {
63464                 n = f.getName();
63465                 key = o[n];
63466                 val = f.getValue();
63467
63468                 if(Ext.isDefined(key)){
63469                     if(Ext.isArray(key)){
63470                         o[n].push(val);
63471                     }else{
63472                         o[n] = [key, val];
63473                     }
63474                 }else{
63475                     o[n] = val;
63476                 }
63477             }
63478         });
63479         return o;
63480     },
63481
63482     /**
63483      * Clears all invalid messages in this form.
63484      * @return {BasicForm} this
63485      */
63486     clearInvalid : function(){
63487         this.items.each(function(f){
63488            f.clearInvalid();
63489         });
63490         return this;
63491     },
63492
63493     /**
63494      * Resets this form.
63495      * @return {BasicForm} this
63496      */
63497     reset : function(){
63498         this.items.each(function(f){
63499             f.reset();
63500         });
63501         return this;
63502     },
63503
63504     /**
63505      * Add Ext.form Components to this form's Collection. This does not result in rendering of
63506      * the passed Component, it just enables the form to validate Fields, and distribute values to
63507      * Fields.
63508      * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
63509      * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
63510      * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
63511      * collection.</b></p>
63512      * @param {Field} field1
63513      * @param {Field} field2 (optional)
63514      * @param {Field} etc (optional)
63515      * @return {BasicForm} this
63516      */
63517     add : function(){
63518         this.items.addAll(Array.prototype.slice.call(arguments, 0));
63519         return this;
63520     },
63521
63522     /**
63523      * Removes a field from the items collection (does NOT remove its markup).
63524      * @param {Field} field
63525      * @return {BasicForm} this
63526      */
63527     remove : function(field){
63528         this.items.remove(field);
63529         return this;
63530     },
63531
63532     /**
63533      * Removes all fields from the collection that have been destroyed.
63534      */
63535     cleanDestroyed : function() {
63536         this.items.filterBy(function(o) { return !!o.isDestroyed; }).each(this.remove, this);
63537     },
63538
63539     /**
63540      * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
63541      * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
63542      * @return {BasicForm} this
63543      */
63544     render : function(){
63545         this.items.each(function(f){
63546             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
63547                 f.applyToMarkup(f.id);
63548             }
63549         });
63550         return this;
63551     },
63552
63553     /**
63554      * Calls {@link Ext#apply} for all fields in this form with the passed object.
63555      * @param {Object} values
63556      * @return {BasicForm} this
63557      */
63558     applyToFields : function(o){
63559         this.items.each(function(f){
63560            Ext.apply(f, o);
63561         });
63562         return this;
63563     },
63564
63565     /**
63566      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
63567      * @param {Object} values
63568      * @return {BasicForm} this
63569      */
63570     applyIfToFields : function(o){
63571         this.items.each(function(f){
63572            Ext.applyIf(f, o);
63573         });
63574         return this;
63575     },
63576
63577     callFieldMethod : function(fnName, args){
63578         args = args || [];
63579         this.items.each(function(f){
63580             if(Ext.isFunction(f[fnName])){
63581                 f[fnName].apply(f, args);
63582             }
63583         });
63584         return this;
63585     }
63586 });
63587
63588 // back compat
63589 Ext.BasicForm = Ext.form.BasicForm;
63590 /**
63591  * @class Ext.form.FormPanel
63592  * @extends Ext.Panel
63593  * <p>Standard form container.</p>
63594  *
63595  * <p><b><u>Layout</u></b></p>
63596  * <p>By default, FormPanel is configured with <tt>layout:'form'</tt> to use an {@link Ext.layout.FormLayout}
63597  * layout manager, which styles and renders fields and labels correctly. When nesting additional Containers
63598  * within a FormPanel, you should ensure that any descendant Containers which host input Fields use the
63599  * {@link Ext.layout.FormLayout} layout manager.</p>
63600  *
63601  * <p><b><u>BasicForm</u></b></p>
63602  * <p>Although <b>not listed</b> as configuration options of FormPanel, the FormPanel class accepts all
63603  * of the config options required to configure its internal {@link Ext.form.BasicForm} for:
63604  * <div class="mdetail-params"><ul>
63605  * <li>{@link Ext.form.BasicForm#fileUpload file uploads}</li>
63606  * <li>functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form</li>
63607  * </ul></div>
63608  *
63609  * <p><b>Note</b>: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
63610  * the <tt><b>initialConfig</b></tt> property of the FormPanel. Applying {@link Ext.form.BasicForm BasicForm}
63611  * configuration settings to <b><tt>this</tt></b> will <b>not</b> affect the BasicForm's configuration.</p>
63612  *
63613  * <p><b><u>Form Validation</u></b></p>
63614  * <p>For information on form validation see the following:</p>
63615  * <div class="mdetail-params"><ul>
63616  * <li>{@link Ext.form.TextField}</li>
63617  * <li>{@link Ext.form.VTypes}</li>
63618  * <li>{@link Ext.form.BasicForm#doAction BasicForm.doAction <b>clientValidation</b> notes}</li>
63619  * <li><tt>{@link Ext.form.FormPanel#monitorValid monitorValid}</tt></li>
63620  * </ul></div>
63621  *
63622  * <p><b><u>Form Submission</u></b></p>
63623  * <p>By default, Ext Forms are submitted through Ajax, using {@link Ext.form.Action}. To enable normal browser
63624  * submission of the {@link Ext.form.BasicForm BasicForm} contained in this FormPanel, see the
63625  * <tt><b>{@link Ext.form.BasicForm#standardSubmit standardSubmit}</b></tt> option.</p>
63626  *
63627  * @constructor
63628  * @param {Object} config Configuration options
63629  * @xtype form
63630  */
63631 Ext.FormPanel = Ext.extend(Ext.Panel, {
63632     /**
63633      * @cfg {String} formId (optional) The id of the FORM tag (defaults to an auto-generated id).
63634      */
63635     /**
63636      * @cfg {Boolean} hideLabels
63637      * <p><tt>true</tt> to hide field labels by default (sets <tt>display:none</tt>). Defaults to
63638      * <tt>false</tt>.</p>
63639      * <p>Also see {@link Ext.Component}.<tt>{@link Ext.Component#hideLabel hideLabel}</tt>.
63640      */
63641     /**
63642      * @cfg {Number} labelPad
63643      * The default padding in pixels for field labels (defaults to <tt>5</tt>). <tt>labelPad</tt> only
63644      * applies if <tt>{@link #labelWidth}</tt> is also specified, otherwise it will be ignored.
63645      */
63646     /**
63647      * @cfg {String} labelSeparator
63648      * See {@link Ext.Component}.<tt>{@link Ext.Component#labelSeparator labelSeparator}</tt>
63649      */
63650     /**
63651      * @cfg {Number} labelWidth The width of labels in pixels. This property cascades to child containers
63652      * and can be overridden on any child container (e.g., a fieldset can specify a different <tt>labelWidth</tt>
63653      * for its fields) (defaults to <tt>100</tt>).
63654      */
63655     /**
63656      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
63657      */
63658     /**
63659      * @cfg {Array} buttons
63660      * An array of {@link Ext.Button}s or {@link Ext.Button} configs used to add buttons to the footer of this FormPanel.<br>
63661      * <p>Buttons in the footer of a FormPanel may be configured with the option <tt>formBind: true</tt>. This causes
63662      * the form's {@link #monitorValid valid state monitor task} to enable/disable those Buttons depending on
63663      * the form's valid/invalid state.</p>
63664      */
63665
63666
63667     /**
63668      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to <tt>75</tt>).
63669      */
63670     minButtonWidth : 75,
63671
63672     /**
63673      * @cfg {String} labelAlign The label alignment value used for the <tt>text-align</tt> specification
63674      * for the <b>container</b>. Valid values are <tt>"left</tt>", <tt>"top"</tt> or <tt>"right"</tt>
63675      * (defaults to <tt>"left"</tt>). This property cascades to child <b>containers</b> and can be
63676      * overridden on any child <b>container</b> (e.g., a fieldset can specify a different <tt>labelAlign</tt>
63677      * for its fields).
63678      */
63679     labelAlign : 'left',
63680
63681     /**
63682      * @cfg {Boolean} monitorValid If <tt>true</tt>, the form monitors its valid state <b>client-side</b> and
63683      * regularly fires the {@link #clientvalidation} event passing that state.<br>
63684      * <p>When monitoring valid state, the FormPanel enables/disables any of its configured
63685      * {@link #buttons} which have been configured with <code>formBind: true</code> depending
63686      * on whether the {@link Ext.form.BasicForm#isValid form is valid} or not. Defaults to <tt>false</tt></p>
63687      */
63688     monitorValid : false,
63689
63690     /**
63691      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
63692      */
63693     monitorPoll : 200,
63694
63695     /**
63696      * @cfg {String} layout Defaults to <tt>'form'</tt>.  Normally this configuration property should not be altered.
63697      * For additional details see {@link Ext.layout.FormLayout} and {@link Ext.Container#layout Ext.Container.layout}.
63698      */
63699     layout : 'form',
63700
63701     // private
63702     initComponent : function(){
63703         this.form = this.createForm();
63704         Ext.FormPanel.superclass.initComponent.call(this);
63705
63706         this.bodyCfg = {
63707             tag: 'form',
63708             cls: this.baseCls + '-body',
63709             method : this.method || 'POST',
63710             id : this.formId || Ext.id()
63711         };
63712         if(this.fileUpload) {
63713             this.bodyCfg.enctype = 'multipart/form-data';
63714         }
63715         this.initItems();
63716
63717         this.addEvents(
63718             /**
63719              * @event clientvalidation
63720              * If the monitorValid config option is true, this event fires repetitively to notify of valid state
63721              * @param {Ext.form.FormPanel} this
63722              * @param {Boolean} valid true if the form has passed client-side validation
63723              */
63724             'clientvalidation'
63725         );
63726
63727         this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']);
63728     },
63729
63730     // private
63731     createForm : function(){
63732         var config = Ext.applyIf({listeners: {}}, this.initialConfig);
63733         return new Ext.form.BasicForm(null, config);
63734     },
63735
63736     // private
63737     initFields : function(){
63738         var f = this.form;
63739         var formPanel = this;
63740         var fn = function(c){
63741             if(formPanel.isField(c)){
63742                 f.add(c);
63743             }else if(c.findBy && c != formPanel){
63744                 formPanel.applySettings(c);
63745                 //each check required for check/radio groups.
63746                 if(c.items && c.items.each){
63747                     c.items.each(fn, this);
63748                 }
63749             }
63750         };
63751         this.items.each(fn, this);
63752     },
63753
63754     // private
63755     applySettings: function(c){
63756         var ct = c.ownerCt;
63757         Ext.applyIf(c, {
63758             labelAlign: ct.labelAlign,
63759             labelWidth: ct.labelWidth,
63760             itemCls: ct.itemCls
63761         });
63762     },
63763
63764     // private
63765     getLayoutTarget : function(){
63766         return this.form.el;
63767     },
63768
63769     /**
63770      * Provides access to the {@link Ext.form.BasicForm Form} which this Panel contains.
63771      * @return {Ext.form.BasicForm} The {@link Ext.form.BasicForm Form} which this Panel contains.
63772      */
63773     getForm : function(){
63774         return this.form;
63775     },
63776
63777     // private
63778     onRender : function(ct, position){
63779         this.initFields();
63780         Ext.FormPanel.superclass.onRender.call(this, ct, position);
63781         this.form.initEl(this.body);
63782     },
63783
63784     // private
63785     beforeDestroy : function(){
63786         this.stopMonitoring();
63787         this.form.destroy(true);
63788         Ext.FormPanel.superclass.beforeDestroy.call(this);
63789     },
63790
63791     // Determine if a Component is usable as a form Field.
63792     isField : function(c) {
63793         return !!c.setValue && !!c.getValue && !!c.markInvalid && !!c.clearInvalid;
63794     },
63795
63796     // private
63797     initEvents : function(){
63798         Ext.FormPanel.superclass.initEvents.call(this);
63799         // Listeners are required here to catch bubbling events from children.
63800         this.on({
63801             scope: this,
63802             add: this.onAddEvent,
63803             remove: this.onRemoveEvent
63804         });
63805         if(this.monitorValid){ // initialize after render
63806             this.startMonitoring();
63807         }
63808     },
63809
63810     // private
63811     onAdd: function(c){
63812         Ext.FormPanel.superclass.onAdd.call(this, c);
63813         this.processAdd(c);
63814     },
63815
63816     // private
63817     onAddEvent: function(ct, c){
63818         if(ct !== this){
63819             this.processAdd(c);
63820         }
63821     },
63822
63823     // private
63824     processAdd : function(c){
63825         // If a single form Field, add it
63826         if(this.isField(c)){
63827             this.form.add(c);
63828         // If a Container, add any Fields it might contain
63829         }else if(c.findBy){
63830             this.applySettings(c);
63831             this.form.add.apply(this.form, c.findBy(this.isField));
63832         }
63833     },
63834
63835     // private
63836     onRemove: function(c){
63837         Ext.FormPanel.superclass.onRemove.call(this, c);
63838         this.processRemove(c);
63839     },
63840
63841     onRemoveEvent: function(ct, c){
63842         if(ct !== this){
63843             this.processRemove(c);
63844         }
63845     },
63846
63847     // private
63848     processRemove: function(c){
63849         if(!this.destroying){
63850             // If a single form Field, remove it
63851             if(this.isField(c)){
63852                 this.form.remove(c);
63853             // If a Container, its already destroyed by the time it gets here.  Remove any references to destroyed fields.
63854             }else if (c.findBy){
63855                 Ext.each(c.findBy(this.isField), this.form.remove, this.form);
63856                 if (c.isDestroyed) {
63857                     this.form.cleanDestroyed();
63858                 }
63859             }
63860         }
63861     },
63862
63863     /**
63864      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
63865      * option "monitorValid"
63866      */
63867     startMonitoring : function(){
63868         if(!this.validTask){
63869             this.validTask = new Ext.util.TaskRunner();
63870             this.validTask.start({
63871                 run : this.bindHandler,
63872                 interval : this.monitorPoll || 200,
63873                 scope: this
63874             });
63875         }
63876     },
63877
63878     /**
63879      * Stops monitoring of the valid state of this form
63880      */
63881     stopMonitoring : function(){
63882         if(this.validTask){
63883             this.validTask.stopAll();
63884             this.validTask = null;
63885         }
63886     },
63887
63888     /**
63889      * This is a proxy for the underlying BasicForm's {@link Ext.form.BasicForm#load} call.
63890      * @param {Object} options The options to pass to the action (see {@link Ext.form.BasicForm#doAction} for details)
63891      */
63892     load : function(){
63893         this.form.load.apply(this.form, arguments);
63894     },
63895
63896     // private
63897     onDisable : function(){
63898         Ext.FormPanel.superclass.onDisable.call(this);
63899         if(this.form){
63900             this.form.items.each(function(){
63901                  this.disable();
63902             });
63903         }
63904     },
63905
63906     // private
63907     onEnable : function(){
63908         Ext.FormPanel.superclass.onEnable.call(this);
63909         if(this.form){
63910             this.form.items.each(function(){
63911                  this.enable();
63912             });
63913         }
63914     },
63915
63916     // private
63917     bindHandler : function(){
63918         var valid = true;
63919         this.form.items.each(function(f){
63920             if(!f.isValid(true)){
63921                 valid = false;
63922                 return false;
63923             }
63924         });
63925         if(this.fbar){
63926             var fitems = this.fbar.items.items;
63927             for(var i = 0, len = fitems.length; i < len; i++){
63928                 var btn = fitems[i];
63929                 if(btn.formBind === true && btn.disabled === valid){
63930                     btn.setDisabled(!valid);
63931                 }
63932             }
63933         }
63934         this.fireEvent('clientvalidation', this, valid);
63935     }
63936 });
63937 Ext.reg('form', Ext.FormPanel);
63938
63939 Ext.form.FormPanel = Ext.FormPanel;
63940 /**
63941  * @class Ext.form.FieldSet
63942  * @extends Ext.Panel
63943  * Standard container used for grouping items within a {@link Ext.form.FormPanel form}.
63944  * <pre><code>
63945 var form = new Ext.FormPanel({
63946     title: 'Simple Form with FieldSets',
63947     labelWidth: 75, // label settings here cascade unless overridden
63948     url: 'save-form.php',
63949     frame:true,
63950     bodyStyle:'padding:5px 5px 0',
63951     width: 700,
63952     renderTo: document.body,
63953     layout:'column', // arrange items in columns
63954     defaults: {      // defaults applied to items
63955         layout: 'form',
63956         border: false,
63957         bodyStyle: 'padding:4px'
63958     },
63959     items: [{
63960         // Fieldset in Column 1
63961         xtype:'fieldset',
63962         columnWidth: 0.5,
63963         title: 'Fieldset 1',
63964         collapsible: true,
63965         autoHeight:true,
63966         defaults: {
63967             anchor: '-20' // leave room for error icon
63968         },
63969         defaultType: 'textfield',
63970         items :[{
63971                 fieldLabel: 'Field 1'
63972             }, {
63973                 fieldLabel: 'Field 2'
63974             }, {
63975                 fieldLabel: 'Field 3'
63976             }
63977         ]
63978     },{
63979         // Fieldset in Column 2 - Panel inside
63980         xtype:'fieldset',
63981         title: 'Show Panel', // title, header, or checkboxToggle creates fieldset header
63982         autoHeight:true,
63983         columnWidth: 0.5,
63984         checkboxToggle: true,
63985         collapsed: true, // fieldset initially collapsed
63986         layout:'anchor',
63987         items :[{
63988             xtype: 'panel',
63989             anchor: '100%',
63990             title: 'Panel inside a fieldset',
63991             frame: true,
63992             height: 100
63993         }]
63994     }]
63995 });
63996  * </code></pre>
63997  * @constructor
63998  * @param {Object} config Configuration options
63999  * @xtype fieldset
64000  */
64001 Ext.form.FieldSet = Ext.extend(Ext.Panel, {
64002     /**
64003      * @cfg {Mixed} checkboxToggle <tt>true</tt> to render a checkbox into the fieldset frame just
64004      * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
64005      * to <tt>false</tt>).
64006      * <p>A {@link Ext.DomHelper DomHelper} element spec may also be specified to create the checkbox.
64007      * If <tt>true</tt> is specified, the default DomHelper config object used to create the element
64008      * is:</p><pre><code>
64009      * {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'}
64010      * </code></pre>
64011      */
64012     /**
64013      * @cfg {String} checkboxName The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>
64014      * (defaults to <tt>'[checkbox id]-checkbox'</tt>).
64015      */
64016     /**
64017      * @cfg {Boolean} collapsible
64018      * <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically
64019      * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse
64020      * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.
64021      */
64022     /**
64023      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
64024      */
64025     /**
64026      * @cfg {String} itemCls A css class to apply to the <tt>x-form-item</tt> of fields (see
64027      * {@link Ext.layout.FormLayout}.{@link Ext.layout.FormLayout#fieldTpl fieldTpl} for details).
64028      * This property cascades to child containers.
64029      */
64030     /**
64031      * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).
64032      */
64033     baseCls : 'x-fieldset',
64034     /**
64035      * @cfg {String} layout The {@link Ext.Container#layout} to use inside the fieldset (defaults to <tt>'form'</tt>).
64036      */
64037     layout : 'form',
64038     /**
64039      * @cfg {Boolean} animCollapse
64040      * <tt>true</tt> to animate the transition when the panel is collapsed, <tt>false</tt> to skip the
64041      * animation (defaults to <tt>false</tt>).
64042      */
64043     animCollapse : false,
64044
64045     // private
64046     onRender : function(ct, position){
64047         if(!this.el){
64048             this.el = document.createElement('fieldset');
64049             this.el.id = this.id;
64050             if (this.title || this.header || this.checkboxToggle) {
64051                 this.el.appendChild(document.createElement('legend')).className = this.baseCls + '-header';
64052             }
64053         }
64054
64055         Ext.form.FieldSet.superclass.onRender.call(this, ct, position);
64056
64057         if(this.checkboxToggle){
64058             var o = typeof this.checkboxToggle == 'object' ?
64059                     this.checkboxToggle :
64060                     {tag: 'input', type: 'checkbox', name: this.checkboxName || this.id+'-checkbox'};
64061             this.checkbox = this.header.insertFirst(o);
64062             this.checkbox.dom.checked = !this.collapsed;
64063             this.mon(this.checkbox, 'click', this.onCheckClick, this);
64064         }
64065     },
64066
64067     // private
64068     onCollapse : function(doAnim, animArg){
64069         if(this.checkbox){
64070             this.checkbox.dom.checked = false;
64071         }
64072         Ext.form.FieldSet.superclass.onCollapse.call(this, doAnim, animArg);
64073
64074     },
64075
64076     // private
64077     onExpand : function(doAnim, animArg){
64078         if(this.checkbox){
64079             this.checkbox.dom.checked = true;
64080         }
64081         Ext.form.FieldSet.superclass.onExpand.call(this, doAnim, animArg);
64082     },
64083
64084     /**
64085      * This function is called by the fieldset's checkbox when it is toggled (only applies when
64086      * checkboxToggle = true).  This method should never be called externally, but can be
64087      * overridden to provide custom behavior when the checkbox is toggled if needed.
64088      */
64089     onCheckClick : function(){
64090         this[this.checkbox.dom.checked ? 'expand' : 'collapse']();
64091     }
64092
64093     /**
64094      * @cfg {String/Number} activeItem
64095      * @hide
64096      */
64097     /**
64098      * @cfg {Mixed} applyTo
64099      * @hide
64100      */
64101     /**
64102      * @cfg {Boolean} bodyBorder
64103      * @hide
64104      */
64105     /**
64106      * @cfg {Boolean} border
64107      * @hide
64108      */
64109     /**
64110      * @cfg {Boolean/Number} bufferResize
64111      * @hide
64112      */
64113     /**
64114      * @cfg {Boolean} collapseFirst
64115      * @hide
64116      */
64117     /**
64118      * @cfg {String} defaultType
64119      * @hide
64120      */
64121     /**
64122      * @cfg {String} disabledClass
64123      * @hide
64124      */
64125     /**
64126      * @cfg {String} elements
64127      * @hide
64128      */
64129     /**
64130      * @cfg {Boolean} floating
64131      * @hide
64132      */
64133     /**
64134      * @cfg {Boolean} footer
64135      * @hide
64136      */
64137     /**
64138      * @cfg {Boolean} frame
64139      * @hide
64140      */
64141     /**
64142      * @cfg {Boolean} header
64143      * @hide
64144      */
64145     /**
64146      * @cfg {Boolean} headerAsText
64147      * @hide
64148      */
64149     /**
64150      * @cfg {Boolean} hideCollapseTool
64151      * @hide
64152      */
64153     /**
64154      * @cfg {String} iconCls
64155      * @hide
64156      */
64157     /**
64158      * @cfg {Boolean/String} shadow
64159      * @hide
64160      */
64161     /**
64162      * @cfg {Number} shadowOffset
64163      * @hide
64164      */
64165     /**
64166      * @cfg {Boolean} shim
64167      * @hide
64168      */
64169     /**
64170      * @cfg {Object/Array} tbar
64171      * @hide
64172      */
64173     /**
64174      * @cfg {Array} tools
64175      * @hide
64176      */
64177     /**
64178      * @cfg {Ext.Template/Ext.XTemplate} toolTemplate
64179      * @hide
64180      */
64181     /**
64182      * @cfg {String} xtype
64183      * @hide
64184      */
64185     /**
64186      * @property header
64187      * @hide
64188      */
64189     /**
64190      * @property footer
64191      * @hide
64192      */
64193     /**
64194      * @method focus
64195      * @hide
64196      */
64197     /**
64198      * @method getBottomToolbar
64199      * @hide
64200      */
64201     /**
64202      * @method getTopToolbar
64203      * @hide
64204      */
64205     /**
64206      * @method setIconClass
64207      * @hide
64208      */
64209     /**
64210      * @event activate
64211      * @hide
64212      */
64213     /**
64214      * @event beforeclose
64215      * @hide
64216      */
64217     /**
64218      * @event bodyresize
64219      * @hide
64220      */
64221     /**
64222      * @event close
64223      * @hide
64224      */
64225     /**
64226      * @event deactivate
64227      * @hide
64228      */
64229 });
64230 Ext.reg('fieldset', Ext.form.FieldSet);/**
64231  * @class Ext.form.HtmlEditor
64232  * @extends Ext.form.Field
64233  * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
64234  * automatically hidden when needed.  These are noted in the config options where appropriate.
64235  * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
64236  * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
64237  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
64238  * supported by this editor.</b>
64239  * <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
64240  * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
64241  * <br><br>Example usage:
64242  * <pre><code>
64243 // Simple example rendered with default options:
64244 Ext.QuickTips.init();  // enable tooltips
64245 new Ext.form.HtmlEditor({
64246     renderTo: Ext.getBody(),
64247     width: 800,
64248     height: 300
64249 });
64250
64251 // Passed via xtype into a container and with custom options:
64252 Ext.QuickTips.init();  // enable tooltips
64253 new Ext.Panel({
64254     title: 'HTML Editor',
64255     renderTo: Ext.getBody(),
64256     width: 600,
64257     height: 300,
64258     frame: true,
64259     layout: 'fit',
64260     items: {
64261         xtype: 'htmleditor',
64262         enableColors: false,
64263         enableAlignments: false
64264     }
64265 });
64266 </code></pre>
64267  * @constructor
64268  * Create a new HtmlEditor
64269  * @param {Object} config
64270  * @xtype htmleditor
64271  */
64272
64273 Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
64274     /**
64275      * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
64276      */
64277     enableFormat : true,
64278     /**
64279      * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
64280      */
64281     enableFontSize : true,
64282     /**
64283      * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
64284      */
64285     enableColors : true,
64286     /**
64287      * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
64288      */
64289     enableAlignments : true,
64290     /**
64291      * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
64292      */
64293     enableLists : true,
64294     /**
64295      * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
64296      */
64297     enableSourceEdit : true,
64298     /**
64299      * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
64300      */
64301     enableLinks : true,
64302     /**
64303      * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
64304      */
64305     enableFont : true,
64306     /**
64307      * @cfg {String} createLinkText The default text for the create link prompt
64308      */
64309     createLinkText : 'Please enter the URL for the link:',
64310     /**
64311      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
64312      */
64313     defaultLinkValue : 'http:/'+'/',
64314     /**
64315      * @cfg {Array} fontFamilies An array of available font families
64316      */
64317     fontFamilies : [
64318         'Arial',
64319         'Courier New',
64320         'Tahoma',
64321         'Times New Roman',
64322         'Verdana'
64323     ],
64324     defaultFont: 'tahoma',
64325     /**
64326      * @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).
64327      */
64328     defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',
64329
64330     // private properties
64331     actionMode: 'wrap',
64332     validationEvent : false,
64333     deferHeight: true,
64334     initialized : false,
64335     activated : false,
64336     sourceEditMode : false,
64337     onFocus : Ext.emptyFn,
64338     iframePad:3,
64339     hideMode:'offsets',
64340     defaultAutoCreate : {
64341         tag: "textarea",
64342         style:"width:500px;height:300px;",
64343         autocomplete: "off"
64344     },
64345
64346     // private
64347     initComponent : function(){
64348         this.addEvents(
64349             /**
64350              * @event initialize
64351              * Fires when the editor is fully initialized (including the iframe)
64352              * @param {HtmlEditor} this
64353              */
64354             'initialize',
64355             /**
64356              * @event activate
64357              * Fires when the editor is first receives the focus. Any insertion must wait
64358              * until after this event.
64359              * @param {HtmlEditor} this
64360              */
64361             'activate',
64362              /**
64363              * @event beforesync
64364              * Fires before the textarea is updated with content from the editor iframe. Return false
64365              * to cancel the sync.
64366              * @param {HtmlEditor} this
64367              * @param {String} html
64368              */
64369             'beforesync',
64370              /**
64371              * @event beforepush
64372              * Fires before the iframe editor is updated with content from the textarea. Return false
64373              * to cancel the push.
64374              * @param {HtmlEditor} this
64375              * @param {String} html
64376              */
64377             'beforepush',
64378              /**
64379              * @event sync
64380              * Fires when the textarea is updated with content from the editor iframe.
64381              * @param {HtmlEditor} this
64382              * @param {String} html
64383              */
64384             'sync',
64385              /**
64386              * @event push
64387              * Fires when the iframe editor is updated with content from the textarea.
64388              * @param {HtmlEditor} this
64389              * @param {String} html
64390              */
64391             'push',
64392              /**
64393              * @event editmodechange
64394              * Fires when the editor switches edit modes
64395              * @param {HtmlEditor} this
64396              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
64397              */
64398             'editmodechange'
64399         );
64400     },
64401
64402     // private
64403     createFontOptions : function(){
64404         var buf = [], fs = this.fontFamilies, ff, lc;
64405         for(var i = 0, len = fs.length; i< len; i++){
64406             ff = fs[i];
64407             lc = ff.toLowerCase();
64408             buf.push(
64409                 '<option value="',lc,'" style="font-family:',ff,';"',
64410                     (this.defaultFont == lc ? ' selected="true">' : '>'),
64411                     ff,
64412                 '</option>'
64413             );
64414         }
64415         return buf.join('');
64416     },
64417
64418     /*
64419      * Protected method that will not generally be called directly. It
64420      * is called when the editor creates its toolbar. Override this method if you need to
64421      * add custom toolbar buttons.
64422      * @param {HtmlEditor} editor
64423      */
64424     createToolbar : function(editor){
64425         var items = [];
64426         var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
64427
64428
64429         function btn(id, toggle, handler){
64430             return {
64431                 itemId : id,
64432                 cls : 'x-btn-icon',
64433                 iconCls: 'x-edit-'+id,
64434                 enableToggle:toggle !== false,
64435                 scope: editor,
64436                 handler:handler||editor.relayBtnCmd,
64437                 clickEvent:'mousedown',
64438                 tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
64439                 overflowText: editor.buttonTips[id].title || undefined,
64440                 tabIndex:-1
64441             };
64442         }
64443
64444
64445         if(this.enableFont && !Ext.isSafari2){
64446             var fontSelectItem = new Ext.Toolbar.Item({
64447                autoEl: {
64448                     tag:'select',
64449                     cls:'x-font-select',
64450                     html: this.createFontOptions()
64451                }
64452             });
64453
64454             items.push(
64455                 fontSelectItem,
64456                 '-'
64457             );
64458         }
64459
64460         if(this.enableFormat){
64461             items.push(
64462                 btn('bold'),
64463                 btn('italic'),
64464                 btn('underline')
64465             );
64466         }
64467
64468         if(this.enableFontSize){
64469             items.push(
64470                 '-',
64471                 btn('increasefontsize', false, this.adjustFont),
64472                 btn('decreasefontsize', false, this.adjustFont)
64473             );
64474         }
64475
64476         if(this.enableColors){
64477             items.push(
64478                 '-', {
64479                     itemId:'forecolor',
64480                     cls:'x-btn-icon',
64481                     iconCls: 'x-edit-forecolor',
64482                     clickEvent:'mousedown',
64483                     tooltip: tipsEnabled ? editor.buttonTips.forecolor || undefined : undefined,
64484                     tabIndex:-1,
64485                     menu : new Ext.menu.ColorMenu({
64486                         allowReselect: true,
64487                         focus: Ext.emptyFn,
64488                         value:'000000',
64489                         plain:true,
64490                         listeners: {
64491                             scope: this,
64492                             select: function(cp, color){
64493                                 this.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
64494                                 this.deferFocus();
64495                             }
64496                         },
64497                         clickEvent:'mousedown'
64498                     })
64499                 }, {
64500                     itemId:'backcolor',
64501                     cls:'x-btn-icon',
64502                     iconCls: 'x-edit-backcolor',
64503                     clickEvent:'mousedown',
64504                     tooltip: tipsEnabled ? editor.buttonTips.backcolor || undefined : undefined,
64505                     tabIndex:-1,
64506                     menu : new Ext.menu.ColorMenu({
64507                         focus: Ext.emptyFn,
64508                         value:'FFFFFF',
64509                         plain:true,
64510                         allowReselect: true,
64511                         listeners: {
64512                             scope: this,
64513                             select: function(cp, color){
64514                                 if(Ext.isGecko){
64515                                     this.execCmd('useCSS', false);
64516                                     this.execCmd('hilitecolor', color);
64517                                     this.execCmd('useCSS', true);
64518                                     this.deferFocus();
64519                                 }else{
64520                                     this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
64521                                     this.deferFocus();
64522                                 }
64523                             }
64524                         },
64525                         clickEvent:'mousedown'
64526                     })
64527                 }
64528             );
64529         }
64530
64531         if(this.enableAlignments){
64532             items.push(
64533                 '-',
64534                 btn('justifyleft'),
64535                 btn('justifycenter'),
64536                 btn('justifyright')
64537             );
64538         }
64539
64540         if(!Ext.isSafari2){
64541             if(this.enableLinks){
64542                 items.push(
64543                     '-',
64544                     btn('createlink', false, this.createLink)
64545                 );
64546             }
64547
64548             if(this.enableLists){
64549                 items.push(
64550                     '-',
64551                     btn('insertorderedlist'),
64552                     btn('insertunorderedlist')
64553                 );
64554             }
64555             if(this.enableSourceEdit){
64556                 items.push(
64557                     '-',
64558                     btn('sourceedit', true, function(btn){
64559                         this.toggleSourceEdit(!this.sourceEditMode);
64560                     })
64561                 );
64562             }
64563         }
64564
64565         // build the toolbar
64566         var tb = new Ext.Toolbar({
64567             renderTo: this.wrap.dom.firstChild,
64568             items: items
64569         });
64570
64571         if (fontSelectItem) {
64572             this.fontSelect = fontSelectItem.el;
64573
64574             this.mon(this.fontSelect, 'change', function(){
64575                 var font = this.fontSelect.dom.value;
64576                 this.relayCmd('fontname', font);
64577                 this.deferFocus();
64578             }, this);
64579         }
64580
64581         // stop form submits
64582         this.mon(tb.el, 'click', function(e){
64583             e.preventDefault();
64584         });
64585
64586         this.tb = tb;
64587         this.tb.doLayout();
64588     },
64589
64590     onDisable: function(){
64591         this.wrap.mask();
64592         Ext.form.HtmlEditor.superclass.onDisable.call(this);
64593     },
64594
64595     onEnable: function(){
64596         this.wrap.unmask();
64597         Ext.form.HtmlEditor.superclass.onEnable.call(this);
64598     },
64599
64600     setReadOnly: function(readOnly){
64601
64602         Ext.form.HtmlEditor.superclass.setReadOnly.call(this, readOnly);
64603         if(this.initialized){
64604             if(Ext.isIE){
64605                 this.getEditorBody().contentEditable = !readOnly;
64606             }else{
64607                 this.setDesignMode(!readOnly);
64608             }
64609             var bd = this.getEditorBody();
64610             if(bd){
64611                 bd.style.cursor = this.readOnly ? 'default' : 'text';
64612             }
64613             this.disableItems(readOnly);
64614         }
64615     },
64616
64617     /**
64618      * Protected method that will not generally be called directly. It
64619      * is called when the editor initializes the iframe with HTML contents. Override this method if you
64620      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
64621      *
64622      * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility
64623      */
64624     getDocMarkup : function(){
64625         var h = Ext.fly(this.iframe).getHeight() - this.iframePad * 2;
64626         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);
64627     },
64628
64629     // private
64630     getEditorBody : function(){
64631         var doc = this.getDoc();
64632         return doc.body || doc.documentElement;
64633     },
64634
64635     // private
64636     getDoc : function(){
64637         return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
64638     },
64639
64640     // private
64641     getWin : function(){
64642         return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
64643     },
64644
64645     // private
64646     onRender : function(ct, position){
64647         Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
64648         this.el.dom.style.border = '0 none';
64649         this.el.dom.setAttribute('tabIndex', -1);
64650         this.el.addClass('x-hidden');
64651         if(Ext.isIE){ // fix IE 1px bogus margin
64652             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;');
64653         }
64654         this.wrap = this.el.wrap({
64655             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
64656         });
64657
64658         this.createToolbar(this);
64659
64660         this.disableItems(true);
64661
64662         this.tb.doLayout();
64663
64664         this.createIFrame();
64665
64666         if(!this.width){
64667             var sz = this.el.getSize();
64668             this.setSize(sz.width, this.height || sz.height);
64669         }
64670         this.resizeEl = this.positionEl = this.wrap;
64671     },
64672
64673     createIFrame: function(){
64674         var iframe = document.createElement('iframe');
64675         iframe.name = Ext.id();
64676         iframe.frameBorder = '0';
64677         iframe.style.overflow = 'auto';
64678
64679         this.wrap.dom.appendChild(iframe);
64680         this.iframe = iframe;
64681
64682         this.monitorTask = Ext.TaskMgr.start({
64683             run: this.checkDesignMode,
64684             scope: this,
64685             interval:100
64686         });
64687     },
64688
64689     initFrame : function(){
64690         Ext.TaskMgr.stop(this.monitorTask);
64691         var doc = this.getDoc();
64692         this.win = this.getWin();
64693
64694         doc.open();
64695         doc.write(this.getDocMarkup());
64696         doc.close();
64697
64698         var task = { // must defer to wait for browser to be ready
64699             run : function(){
64700                 var doc = this.getDoc();
64701                 if(doc.body || doc.readyState == 'complete'){
64702                     Ext.TaskMgr.stop(task);
64703                     this.setDesignMode(true);
64704                     this.initEditor.defer(10, this);
64705                 }
64706             },
64707             interval : 10,
64708             duration:10000,
64709             scope: this
64710         };
64711         Ext.TaskMgr.start(task);
64712     },
64713
64714
64715     checkDesignMode : function(){
64716         if(this.wrap && this.wrap.dom.offsetWidth){
64717             var doc = this.getDoc();
64718             if(!doc){
64719                 return;
64720             }
64721             if(!doc.editorInitialized || this.getDesignMode() != 'on'){
64722                 this.initFrame();
64723             }
64724         }
64725     },
64726
64727     /* private
64728      * set current design mode. To enable, mode can be true or 'on', off otherwise
64729      */
64730     setDesignMode : function(mode){
64731         var doc ;
64732         if(doc = this.getDoc()){
64733             if(this.readOnly){
64734                 mode = false;
64735             }
64736             doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
64737         }
64738
64739     },
64740
64741     // private
64742     getDesignMode : function(){
64743         var doc = this.getDoc();
64744         if(!doc){ return ''; }
64745         return String(doc.designMode).toLowerCase();
64746
64747     },
64748
64749     disableItems: function(disabled){
64750         if(this.fontSelect){
64751             this.fontSelect.dom.disabled = disabled;
64752         }
64753         this.tb.items.each(function(item){
64754             if(item.getItemId() != 'sourceedit'){
64755                 item.setDisabled(disabled);
64756             }
64757         });
64758     },
64759
64760     // private
64761     onResize : function(w, h){
64762         Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
64763         if(this.el && this.iframe){
64764             if(Ext.isNumber(w)){
64765                 var aw = w - this.wrap.getFrameWidth('lr');
64766                 this.el.setWidth(aw);
64767                 this.tb.setWidth(aw);
64768                 this.iframe.style.width = Math.max(aw, 0) + 'px';
64769             }
64770             if(Ext.isNumber(h)){
64771                 var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
64772                 this.el.setHeight(ah);
64773                 this.iframe.style.height = Math.max(ah, 0) + 'px';
64774                 var bd = this.getEditorBody();
64775                 if(bd){
64776                     bd.style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
64777                 }
64778             }
64779         }
64780     },
64781
64782     /**
64783      * Toggles the editor between standard and source edit mode.
64784      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
64785      */
64786     toggleSourceEdit : function(sourceEditMode){
64787         var iframeHeight,
64788             elHeight,
64789             ls;
64790
64791         if (sourceEditMode === undefined) {
64792             sourceEditMode = !this.sourceEditMode;
64793         }
64794         this.sourceEditMode = sourceEditMode === true;
64795         var btn = this.tb.getComponent('sourceedit');
64796
64797         if (btn.pressed !== this.sourceEditMode) {
64798             btn.toggle(this.sourceEditMode);
64799             if (!btn.xtbHidden) {
64800                 return;
64801             }
64802         }
64803         if (this.sourceEditMode) {
64804             // grab the height of the containing panel before we hide the iframe
64805             ls = this.getSize();
64806
64807             iframeHeight = Ext.get(this.iframe).getHeight();
64808
64809             this.disableItems(true);
64810             this.syncValue();
64811             this.iframe.className = 'x-hidden';
64812             this.el.removeClass('x-hidden');
64813             this.el.dom.removeAttribute('tabIndex');
64814             this.el.focus();
64815             this.el.dom.style.height = iframeHeight + 'px';
64816         }
64817         else {
64818             elHeight = parseInt(this.el.dom.style.height, 10);
64819             if (this.initialized) {
64820                 this.disableItems(this.readOnly);
64821             }
64822             this.pushValue();
64823             this.iframe.className = '';
64824             this.el.addClass('x-hidden');
64825             this.el.dom.setAttribute('tabIndex', -1);
64826             this.deferFocus();
64827
64828             this.setSize(ls);
64829             this.iframe.style.height = elHeight + 'px';
64830         }
64831         this.fireEvent('editmodechange', this, this.sourceEditMode);
64832     },
64833
64834     // private used internally
64835     createLink : function() {
64836         var url = prompt(this.createLinkText, this.defaultLinkValue);
64837         if(url && url != 'http:/'+'/'){
64838             this.relayCmd('createlink', url);
64839         }
64840     },
64841
64842     // private
64843     initEvents : function(){
64844         this.originalValue = this.getValue();
64845     },
64846
64847     /**
64848      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
64849      * @method
64850      */
64851     markInvalid : Ext.emptyFn,
64852
64853     /**
64854      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
64855      * @method
64856      */
64857     clearInvalid : Ext.emptyFn,
64858
64859     // docs inherit from Field
64860     setValue : function(v){
64861         Ext.form.HtmlEditor.superclass.setValue.call(this, v);
64862         this.pushValue();
64863         return this;
64864     },
64865
64866     /**
64867      * Protected method that will not generally be called directly. If you need/want
64868      * custom HTML cleanup, this is the method you should override.
64869      * @param {String} html The HTML to be cleaned
64870      * @return {String} The cleaned HTML
64871      */
64872     cleanHtml: function(html) {
64873         html = String(html);
64874         if(Ext.isWebKit){ // strip safari nonsense
64875             html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
64876         }
64877
64878         /*
64879          * Neat little hack. Strips out all the non-digit characters from the default
64880          * value and compares it to the character code of the first character in the string
64881          * because it can cause encoding issues when posted to the server.
64882          */
64883         if(html.charCodeAt(0) == this.defaultValue.replace(/\D/g, '')){
64884             html = html.substring(1);
64885         }
64886         return html;
64887     },
64888
64889     /**
64890      * Protected method that will not generally be called directly. Syncs the contents
64891      * of the editor iframe with the textarea.
64892      */
64893     syncValue : function(){
64894         if(this.initialized){
64895             var bd = this.getEditorBody();
64896             var html = bd.innerHTML;
64897             if(Ext.isWebKit){
64898                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
64899                 var m = bs.match(/text-align:(.*?);/i);
64900                 if(m && m[1]){
64901                     html = '<div style="'+m[0]+'">' + html + '</div>';
64902                 }
64903             }
64904             html = this.cleanHtml(html);
64905             if(this.fireEvent('beforesync', this, html) !== false){
64906                 this.el.dom.value = html;
64907                 this.fireEvent('sync', this, html);
64908             }
64909         }
64910     },
64911
64912     //docs inherit from Field
64913     getValue : function() {
64914         this[this.sourceEditMode ? 'pushValue' : 'syncValue']();
64915         return Ext.form.HtmlEditor.superclass.getValue.call(this);
64916     },
64917
64918     /**
64919      * Protected method that will not generally be called directly. Pushes the value of the textarea
64920      * into the iframe editor.
64921      */
64922     pushValue : function(){
64923         if(this.initialized){
64924             var v = this.el.dom.value;
64925             if(!this.activated && v.length < 1){
64926                 v = this.defaultValue;
64927             }
64928             if(this.fireEvent('beforepush', this, v) !== false){
64929                 this.getEditorBody().innerHTML = v;
64930                 if(Ext.isGecko){
64931                     // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
64932                     this.setDesignMode(false);  //toggle off first
64933                     this.setDesignMode(true);
64934                 }
64935                 this.fireEvent('push', this, v);
64936             }
64937
64938         }
64939     },
64940
64941     // private
64942     deferFocus : function(){
64943         this.focus.defer(10, this);
64944     },
64945
64946     // docs inherit from Field
64947     focus : function(){
64948         if(this.win && !this.sourceEditMode){
64949             this.win.focus();
64950         }else{
64951             this.el.focus();
64952         }
64953     },
64954
64955     // private
64956     initEditor : function(){
64957         //Destroying the component during/before initEditor can cause issues.
64958         try{
64959             var dbody = this.getEditorBody(),
64960                 ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
64961                 doc,
64962                 fn;
64963
64964             ss['background-attachment'] = 'fixed'; // w3c
64965             dbody.bgProperties = 'fixed'; // ie
64966
64967             Ext.DomHelper.applyStyles(dbody, ss);
64968
64969             doc = this.getDoc();
64970
64971             if(doc){
64972                 try{
64973                     Ext.EventManager.removeAll(doc);
64974                 }catch(e){}
64975             }
64976
64977             /*
64978              * We need to use createDelegate here, because when using buffer, the delayed task is added
64979              * as a property to the function. When the listener is removed, the task is deleted from the function.
64980              * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
64981              * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
64982              */
64983             fn = this.onEditorEvent.createDelegate(this);
64984             Ext.EventManager.on(doc, {
64985                 mousedown: fn,
64986                 dblclick: fn,
64987                 click: fn,
64988                 keyup: fn,
64989                 buffer:100
64990             });
64991
64992             if(Ext.isGecko){
64993                 Ext.EventManager.on(doc, 'keypress', this.applyCommand, this);
64994             }
64995             if(Ext.isIE || Ext.isWebKit || Ext.isOpera){
64996                 Ext.EventManager.on(doc, 'keydown', this.fixKeys, this);
64997             }
64998             doc.editorInitialized = true;
64999             this.initialized = true;
65000             this.pushValue();
65001             this.setReadOnly(this.readOnly);
65002             this.fireEvent('initialize', this);
65003         }catch(e){}
65004     },
65005
65006     // private
65007     onDestroy : function(){
65008         if(this.monitorTask){
65009             Ext.TaskMgr.stop(this.monitorTask);
65010         }
65011         if(this.rendered){
65012             Ext.destroy(this.tb);
65013             var doc = this.getDoc();
65014             if(doc){
65015                 try{
65016                     Ext.EventManager.removeAll(doc);
65017                     for (var prop in doc){
65018                         delete doc[prop];
65019                     }
65020                 }catch(e){}
65021             }
65022             if(this.wrap){
65023                 this.wrap.dom.innerHTML = '';
65024                 this.wrap.remove();
65025             }
65026         }
65027
65028         if(this.el){
65029             this.el.removeAllListeners();
65030             this.el.remove();
65031         }
65032         this.purgeListeners();
65033     },
65034
65035     // private
65036     onFirstFocus : function(){
65037         this.activated = true;
65038         this.disableItems(this.readOnly);
65039         if(Ext.isGecko){ // prevent silly gecko errors
65040             this.win.focus();
65041             var s = this.win.getSelection();
65042             if(!s.focusNode || s.focusNode.nodeType != 3){
65043                 var r = s.getRangeAt(0);
65044                 r.selectNodeContents(this.getEditorBody());
65045                 r.collapse(true);
65046                 this.deferFocus();
65047             }
65048             try{
65049                 this.execCmd('useCSS', true);
65050                 this.execCmd('styleWithCSS', false);
65051             }catch(e){}
65052         }
65053         this.fireEvent('activate', this);
65054     },
65055
65056     // private
65057     adjustFont: function(btn){
65058         var adjust = btn.getItemId() == 'increasefontsize' ? 1 : -1,
65059             doc = this.getDoc(),
65060             v = parseInt(doc.queryCommandValue('FontSize') || 2, 10);
65061         if((Ext.isSafari && !Ext.isSafari2) || Ext.isChrome || Ext.isAir){
65062             // Safari 3 values
65063             // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
65064             if(v <= 10){
65065                 v = 1 + adjust;
65066             }else if(v <= 13){
65067                 v = 2 + adjust;
65068             }else if(v <= 16){
65069                 v = 3 + adjust;
65070             }else if(v <= 18){
65071                 v = 4 + adjust;
65072             }else if(v <= 24){
65073                 v = 5 + adjust;
65074             }else {
65075                 v = 6 + adjust;
65076             }
65077             v = v.constrain(1, 6);
65078         }else{
65079             if(Ext.isSafari){ // safari
65080                 adjust *= 2;
65081             }
65082             v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
65083         }
65084         this.execCmd('FontSize', v);
65085     },
65086
65087     // private
65088     onEditorEvent : function(e){
65089         this.updateToolbar();
65090     },
65091
65092
65093     /**
65094      * Protected method that will not generally be called directly. It triggers
65095      * a toolbar update by reading the markup state of the current selection in the editor.
65096      */
65097     updateToolbar: function(){
65098
65099         if(this.readOnly){
65100             return;
65101         }
65102
65103         if(!this.activated){
65104             this.onFirstFocus();
65105             return;
65106         }
65107
65108         var btns = this.tb.items.map,
65109             doc = this.getDoc();
65110
65111         if(this.enableFont && !Ext.isSafari2){
65112             var name = (doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
65113             if(name != this.fontSelect.dom.value){
65114                 this.fontSelect.dom.value = name;
65115             }
65116         }
65117         if(this.enableFormat){
65118             btns.bold.toggle(doc.queryCommandState('bold'));
65119             btns.italic.toggle(doc.queryCommandState('italic'));
65120             btns.underline.toggle(doc.queryCommandState('underline'));
65121         }
65122         if(this.enableAlignments){
65123             btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
65124             btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
65125             btns.justifyright.toggle(doc.queryCommandState('justifyright'));
65126         }
65127         if(!Ext.isSafari2 && this.enableLists){
65128             btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
65129             btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
65130         }
65131
65132         Ext.menu.MenuMgr.hideAll();
65133
65134         this.syncValue();
65135     },
65136
65137     // private
65138     relayBtnCmd : function(btn){
65139         this.relayCmd(btn.getItemId());
65140     },
65141
65142     /**
65143      * Executes a Midas editor command on the editor document and performs necessary focus and
65144      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
65145      * @param {String} cmd The Midas command
65146      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
65147      */
65148     relayCmd : function(cmd, value){
65149         (function(){
65150             this.focus();
65151             this.execCmd(cmd, value);
65152             this.updateToolbar();
65153         }).defer(10, this);
65154     },
65155
65156     /**
65157      * Executes a Midas editor command directly on the editor document.
65158      * For visual commands, you should use {@link #relayCmd} instead.
65159      * <b>This should only be called after the editor is initialized.</b>
65160      * @param {String} cmd The Midas command
65161      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
65162      */
65163     execCmd : function(cmd, value){
65164         var doc = this.getDoc();
65165         doc.execCommand(cmd, false, value === undefined ? null : value);
65166         this.syncValue();
65167     },
65168
65169     // private
65170     applyCommand : function(e){
65171         if(e.ctrlKey){
65172             var c = e.getCharCode(), cmd;
65173             if(c > 0){
65174                 c = String.fromCharCode(c);
65175                 switch(c){
65176                     case 'b':
65177                         cmd = 'bold';
65178                     break;
65179                     case 'i':
65180                         cmd = 'italic';
65181                     break;
65182                     case 'u':
65183                         cmd = 'underline';
65184                     break;
65185                 }
65186                 if(cmd){
65187                     this.win.focus();
65188                     this.execCmd(cmd);
65189                     this.deferFocus();
65190                     e.preventDefault();
65191                 }
65192             }
65193         }
65194     },
65195
65196     /**
65197      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
65198      * to insert text.
65199      * @param {String} text
65200      */
65201     insertAtCursor : function(text){
65202         if(!this.activated){
65203             return;
65204         }
65205         if(Ext.isIE){
65206             this.win.focus();
65207             var doc = this.getDoc(),
65208                 r = doc.selection.createRange();
65209             if(r){
65210                 r.pasteHTML(text);
65211                 this.syncValue();
65212                 this.deferFocus();
65213             }
65214         }else{
65215             this.win.focus();
65216             this.execCmd('InsertHTML', text);
65217             this.deferFocus();
65218         }
65219     },
65220
65221     // private
65222     fixKeys : function(){ // load time branching for fastest keydown performance
65223         if(Ext.isIE){
65224             return function(e){
65225                 var k = e.getKey(),
65226                     doc = this.getDoc(),
65227                         r;
65228                 if(k == e.TAB){
65229                     e.stopEvent();
65230                     r = doc.selection.createRange();
65231                     if(r){
65232                         r.collapse(true);
65233                         r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
65234                         this.deferFocus();
65235                     }
65236                 }else if(k == e.ENTER){
65237                     r = doc.selection.createRange();
65238                     if(r){
65239                         var target = r.parentElement();
65240                         if(!target || target.tagName.toLowerCase() != 'li'){
65241                             e.stopEvent();
65242                             r.pasteHTML('<br />');
65243                             r.collapse(false);
65244                             r.select();
65245                         }
65246                     }
65247                 }
65248             };
65249         }else if(Ext.isOpera){
65250             return function(e){
65251                 var k = e.getKey();
65252                 if(k == e.TAB){
65253                     e.stopEvent();
65254                     this.win.focus();
65255                     this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
65256                     this.deferFocus();
65257                 }
65258             };
65259         }else if(Ext.isWebKit){
65260             return function(e){
65261                 var k = e.getKey();
65262                 if(k == e.TAB){
65263                     e.stopEvent();
65264                     this.execCmd('InsertText','\t');
65265                     this.deferFocus();
65266                 }else if(k == e.ENTER){
65267                     e.stopEvent();
65268                     this.execCmd('InsertHtml','<br /><br />');
65269                     this.deferFocus();
65270                 }
65271              };
65272         }
65273     }(),
65274
65275     /**
65276      * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
65277      * @return {Ext.Toolbar}
65278      */
65279     getToolbar : function(){
65280         return this.tb;
65281     },
65282
65283     /**
65284      * Object collection of toolbar tooltips for the buttons in the editor. The key
65285      * is the command id associated with that button and the value is a valid QuickTips object.
65286      * For example:
65287 <pre><code>
65288 {
65289     bold : {
65290         title: 'Bold (Ctrl+B)',
65291         text: 'Make the selected text bold.',
65292         cls: 'x-html-editor-tip'
65293     },
65294     italic : {
65295         title: 'Italic (Ctrl+I)',
65296         text: 'Make the selected text italic.',
65297         cls: 'x-html-editor-tip'
65298     },
65299     ...
65300 </code></pre>
65301     * @type Object
65302      */
65303     buttonTips : {
65304         bold : {
65305             title: 'Bold (Ctrl+B)',
65306             text: 'Make the selected text bold.',
65307             cls: 'x-html-editor-tip'
65308         },
65309         italic : {
65310             title: 'Italic (Ctrl+I)',
65311             text: 'Make the selected text italic.',
65312             cls: 'x-html-editor-tip'
65313         },
65314         underline : {
65315             title: 'Underline (Ctrl+U)',
65316             text: 'Underline the selected text.',
65317             cls: 'x-html-editor-tip'
65318         },
65319         increasefontsize : {
65320             title: 'Grow Text',
65321             text: 'Increase the font size.',
65322             cls: 'x-html-editor-tip'
65323         },
65324         decreasefontsize : {
65325             title: 'Shrink Text',
65326             text: 'Decrease the font size.',
65327             cls: 'x-html-editor-tip'
65328         },
65329         backcolor : {
65330             title: 'Text Highlight Color',
65331             text: 'Change the background color of the selected text.',
65332             cls: 'x-html-editor-tip'
65333         },
65334         forecolor : {
65335             title: 'Font Color',
65336             text: 'Change the color of the selected text.',
65337             cls: 'x-html-editor-tip'
65338         },
65339         justifyleft : {
65340             title: 'Align Text Left',
65341             text: 'Align text to the left.',
65342             cls: 'x-html-editor-tip'
65343         },
65344         justifycenter : {
65345             title: 'Center Text',
65346             text: 'Center text in the editor.',
65347             cls: 'x-html-editor-tip'
65348         },
65349         justifyright : {
65350             title: 'Align Text Right',
65351             text: 'Align text to the right.',
65352             cls: 'x-html-editor-tip'
65353         },
65354         insertunorderedlist : {
65355             title: 'Bullet List',
65356             text: 'Start a bulleted list.',
65357             cls: 'x-html-editor-tip'
65358         },
65359         insertorderedlist : {
65360             title: 'Numbered List',
65361             text: 'Start a numbered list.',
65362             cls: 'x-html-editor-tip'
65363         },
65364         createlink : {
65365             title: 'Hyperlink',
65366             text: 'Make the selected text a hyperlink.',
65367             cls: 'x-html-editor-tip'
65368         },
65369         sourceedit : {
65370             title: 'Source Edit',
65371             text: 'Switch to source editing mode.',
65372             cls: 'x-html-editor-tip'
65373         }
65374     }
65375
65376     // hide stuff that is not compatible
65377     /**
65378      * @event blur
65379      * @hide
65380      */
65381     /**
65382      * @event change
65383      * @hide
65384      */
65385     /**
65386      * @event focus
65387      * @hide
65388      */
65389     /**
65390      * @event specialkey
65391      * @hide
65392      */
65393     /**
65394      * @cfg {String} fieldClass @hide
65395      */
65396     /**
65397      * @cfg {String} focusClass @hide
65398      */
65399     /**
65400      * @cfg {String} autoCreate @hide
65401      */
65402     /**
65403      * @cfg {String} inputType @hide
65404      */
65405     /**
65406      * @cfg {String} invalidClass @hide
65407      */
65408     /**
65409      * @cfg {String} invalidText @hide
65410      */
65411     /**
65412      * @cfg {String} msgFx @hide
65413      */
65414     /**
65415      * @cfg {String} validateOnBlur @hide
65416      */
65417     /**
65418      * @cfg {Boolean} allowDomMove  @hide
65419      */
65420     /**
65421      * @cfg {String} applyTo @hide
65422      */
65423     /**
65424      * @cfg {String} autoHeight  @hide
65425      */
65426     /**
65427      * @cfg {String} autoWidth  @hide
65428      */
65429     /**
65430      * @cfg {String} cls  @hide
65431      */
65432     /**
65433      * @cfg {String} disabled  @hide
65434      */
65435     /**
65436      * @cfg {String} disabledClass  @hide
65437      */
65438     /**
65439      * @cfg {String} msgTarget  @hide
65440      */
65441     /**
65442      * @cfg {String} readOnly  @hide
65443      */
65444     /**
65445      * @cfg {String} style  @hide
65446      */
65447     /**
65448      * @cfg {String} validationDelay  @hide
65449      */
65450     /**
65451      * @cfg {String} validationEvent  @hide
65452      */
65453     /**
65454      * @cfg {String} tabIndex  @hide
65455      */
65456     /**
65457      * @property disabled
65458      * @hide
65459      */
65460     /**
65461      * @method applyToMarkup
65462      * @hide
65463      */
65464     /**
65465      * @method disable
65466      * @hide
65467      */
65468     /**
65469      * @method enable
65470      * @hide
65471      */
65472     /**
65473      * @method validate
65474      * @hide
65475      */
65476     /**
65477      * @event valid
65478      * @hide
65479      */
65480     /**
65481      * @method setDisabled
65482      * @hide
65483      */
65484     /**
65485      * @cfg keys
65486      * @hide
65487      */
65488 });
65489 Ext.reg('htmleditor', Ext.form.HtmlEditor);
65490 /**
65491  * @class Ext.form.TimeField
65492  * @extends Ext.form.ComboBox
65493  * Provides a time input field with a time dropdown and automatic time validation.  Example usage:
65494  * <pre><code>
65495 new Ext.form.TimeField({
65496     minValue: '9:00 AM',
65497     maxValue: '6:00 PM',
65498     increment: 30
65499 });
65500 </code></pre>
65501  * @constructor
65502  * Create a new TimeField
65503  * @param {Object} config
65504  * @xtype timefield
65505  */
65506 Ext.form.TimeField = Ext.extend(Ext.form.ComboBox, {
65507     /**
65508      * @cfg {Date/String} minValue
65509      * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
65510      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
65511      */
65512     minValue : undefined,
65513     /**
65514      * @cfg {Date/String} maxValue
65515      * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
65516      * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
65517      */
65518     maxValue : undefined,
65519     /**
65520      * @cfg {String} minText
65521      * The error text to display when the date in the cell is before minValue (defaults to
65522      * 'The time in this field must be equal to or after {0}').
65523      */
65524     minText : "The time in this field must be equal to or after {0}",
65525     /**
65526      * @cfg {String} maxText
65527      * The error text to display when the time is after maxValue (defaults to
65528      * 'The time in this field must be equal to or before {0}').
65529      */
65530     maxText : "The time in this field must be equal to or before {0}",
65531     /**
65532      * @cfg {String} invalidText
65533      * The error text to display when the time in the field is invalid (defaults to
65534      * '{value} is not a valid time').
65535      */
65536     invalidText : "{0} is not a valid time",
65537     /**
65538      * @cfg {String} format
65539      * The default time format string which can be overriden for localization support.  The format must be
65540      * valid according to {@link Date#parseDate} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time
65541      * format try 'H:i' instead.
65542      */
65543     format : "g:i A",
65544     /**
65545      * @cfg {String} altFormats
65546      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
65547      * 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').
65548      */
65549     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",
65550     /**
65551      * @cfg {Number} increment
65552      * The number of minutes between each time value in the list (defaults to 15).
65553      */
65554     increment: 15,
65555
65556     // private override
65557     mode: 'local',
65558     // private override
65559     triggerAction: 'all',
65560     // private override
65561     typeAhead: false,
65562
65563     // private - This is the date to use when generating time values in the absence of either minValue
65564     // or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an
65565     // arbitrary "safe" date that can be any date aside from DST boundary dates.
65566     initDate: '1/1/2008',
65567
65568     initDateFormat: 'j/n/Y',
65569
65570     // private
65571     initComponent : function(){
65572         if(Ext.isDefined(this.minValue)){
65573             this.setMinValue(this.minValue, true);
65574         }
65575         if(Ext.isDefined(this.maxValue)){
65576             this.setMaxValue(this.maxValue, true);
65577         }
65578         if(!this.store){
65579             this.generateStore(true);
65580         }
65581         Ext.form.TimeField.superclass.initComponent.call(this);
65582     },
65583
65584     /**
65585      * Replaces any existing {@link #minValue} with the new time and refreshes the store.
65586      * @param {Date/String} value The minimum time that can be selected
65587      */
65588     setMinValue: function(value, /* private */ initial){
65589         this.setLimit(value, true, initial);
65590         return this;
65591     },
65592
65593     /**
65594      * Replaces any existing {@link #maxValue} with the new time and refreshes the store.
65595      * @param {Date/String} value The maximum time that can be selected
65596      */
65597     setMaxValue: function(value, /* private */ initial){
65598         this.setLimit(value, false, initial);
65599         return this;
65600     },
65601
65602     // private
65603     generateStore: function(initial){
65604         var min = this.minValue || new Date(this.initDate).clearTime(),
65605             max = this.maxValue || new Date(this.initDate).clearTime().add('mi', (24 * 60) - 1),
65606             times = [];
65607
65608         while(min <= max){
65609             times.push(min.dateFormat(this.format));
65610             min = min.add('mi', this.increment);
65611         }
65612         this.bindStore(times, initial);
65613     },
65614
65615     // private
65616     setLimit: function(value, isMin, initial){
65617         var d;
65618         if(Ext.isString(value)){
65619             d = this.parseDate(value);
65620         }else if(Ext.isDate(value)){
65621             d = value;
65622         }
65623         if(d){
65624             var val = new Date(this.initDate).clearTime();
65625             val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
65626             this[isMin ? 'minValue' : 'maxValue'] = val;
65627             if(!initial){
65628                 this.generateStore();
65629             }
65630         }
65631     },
65632
65633     // inherited docs
65634     getValue : function(){
65635         var v = Ext.form.TimeField.superclass.getValue.call(this);
65636         return this.formatDate(this.parseDate(v)) || '';
65637     },
65638
65639     // inherited docs
65640     setValue : function(value){
65641         return Ext.form.TimeField.superclass.setValue.call(this, this.formatDate(this.parseDate(value)));
65642     },
65643
65644     // private overrides
65645     validateValue : Ext.form.DateField.prototype.validateValue,
65646
65647     formatDate : Ext.form.DateField.prototype.formatDate,
65648
65649     parseDate: function(value) {
65650         if (!value || Ext.isDate(value)) {
65651             return value;
65652         }
65653
65654         var id = this.initDate + ' ',
65655             idf = this.initDateFormat + ' ',
65656             v = Date.parseDate(id + value, idf + this.format), // *** handle DST. note: this.format is a TIME-only format
65657             af = this.altFormats;
65658
65659         if (!v && af) {
65660             if (!this.altFormatsArray) {
65661                 this.altFormatsArray = af.split("|");
65662             }
65663             for (var i = 0, afa = this.altFormatsArray, len = afa.length; i < len && !v; i++) {
65664                 v = Date.parseDate(id + value, idf + afa[i]);
65665             }
65666         }
65667
65668         return v;
65669     }
65670 });
65671 Ext.reg('timefield', Ext.form.TimeField);/**
65672  * @class Ext.form.SliderField
65673  * @extends Ext.form.Field
65674  * Wraps a {@link Ext.Slider Slider} so it can be used as a form field.
65675  * @constructor
65676  * Creates a new SliderField
65677  * @param {Object} config Configuration options. Note that you can pass in any slider configuration options, as well as
65678  * as any field configuration options.
65679  * @xtype sliderfield
65680  */
65681 Ext.form.SliderField = Ext.extend(Ext.form.Field, {
65682     
65683     /**
65684      * @cfg {Boolean} useTips
65685      * True to use an Ext.slider.Tip to display tips for the value. Defaults to <tt>true</tt>.
65686      */
65687     useTips : true,
65688     
65689     /**
65690      * @cfg {Function} tipText
65691      * A function used to display custom text for the slider tip. Defaults to <tt>null</tt>, which will
65692      * use the default on the plugin.
65693      */
65694     tipText : null,
65695     
65696     // private override
65697     actionMode: 'wrap',
65698     
65699     /**
65700      * Initialize the component.
65701      * @private
65702      */
65703     initComponent : function() {
65704         var cfg = Ext.copyTo({
65705             id: this.id + '-slider'
65706         }, this.initialConfig, ['vertical', 'minValue', 'maxValue', 'decimalPrecision', 'keyIncrement', 'increment', 'clickToChange', 'animate']);
65707         
65708         // only can use it if it exists.
65709         if (this.useTips) {
65710             var plug = this.tipText ? {getText: this.tipText} : {};
65711             cfg.plugins = [new Ext.slider.Tip(plug)];
65712         }
65713         this.slider = new Ext.Slider(cfg);
65714         Ext.form.SliderField.superclass.initComponent.call(this);
65715     },    
65716     
65717     /**
65718      * Set up the hidden field
65719      * @param {Object} ct The container to render to.
65720      * @param {Object} position The position in the container to render to.
65721      * @private
65722      */
65723     onRender : function(ct, position){
65724         this.autoCreate = {
65725             id: this.id,
65726             name: this.name,
65727             type: 'hidden',
65728             tag: 'input'    
65729         };
65730         Ext.form.SliderField.superclass.onRender.call(this, ct, position);
65731         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
65732         this.resizeEl = this.positionEl = this.wrap;
65733         this.slider.render(this.wrap);
65734     },
65735     
65736     /**
65737      * Ensure that the slider size is set automatically when the field resizes.
65738      * @param {Object} w The width
65739      * @param {Object} h The height
65740      * @param {Object} aw The adjusted width
65741      * @param {Object} ah The adjusted height
65742      * @private
65743      */
65744     onResize : function(w, h, aw, ah){
65745         Ext.form.SliderField.superclass.onResize.call(this, w, h, aw, ah);
65746         this.slider.setSize(w, h);    
65747     },
65748     
65749     /**
65750      * Initialize any events for this class.
65751      * @private
65752      */
65753     initEvents : function(){
65754         Ext.form.SliderField.superclass.initEvents.call(this);
65755         this.slider.on('change', this.onChange, this);   
65756     },
65757     
65758     /**
65759      * Utility method to set the value of the field when the slider changes.
65760      * @param {Object} slider The slider object.
65761      * @param {Object} v The new value.
65762      * @private
65763      */
65764     onChange : function(slider, v){
65765         this.setValue(v, undefined, true);
65766     },
65767     
65768     /**
65769      * Enable the slider when the field is enabled.
65770      * @private
65771      */
65772     onEnable : function(){
65773         Ext.form.SliderField.superclass.onEnable.call(this);
65774         this.slider.enable();
65775     },
65776     
65777     /**
65778      * Disable the slider when the field is disabled.
65779      * @private
65780      */
65781     onDisable : function(){
65782         Ext.form.SliderField.superclass.onDisable.call(this);
65783         this.slider.disable();    
65784     },
65785     
65786     /**
65787      * Ensure the slider is destroyed when the field is destroyed.
65788      * @private
65789      */
65790     beforeDestroy : function(){
65791         Ext.destroy(this.slider);
65792         Ext.form.SliderField.superclass.beforeDestroy.call(this);
65793     },
65794     
65795     /**
65796      * If a side icon is shown, do alignment to the slider
65797      * @private
65798      */
65799     alignErrorIcon : function(){
65800         this.errorIcon.alignTo(this.slider.el, 'tl-tr', [2, 0]);
65801     },
65802     
65803     /**
65804      * Sets the minimum field value.
65805      * @param {Number} v The new minimum value.
65806      * @return {Ext.form.SliderField} this
65807      */
65808     setMinValue : function(v){
65809         this.slider.setMinValue(v);
65810         return this;    
65811     },
65812     
65813     /**
65814      * Sets the maximum field value.
65815      * @param {Number} v The new maximum value.
65816      * @return {Ext.form.SliderField} this
65817      */
65818     setMaxValue : function(v){
65819         this.slider.setMaxValue(v);
65820         return this;    
65821     },
65822     
65823     /**
65824      * Sets the value for this field.
65825      * @param {Number} v The new value.
65826      * @param {Boolean} animate (optional) Whether to animate the transition. If not specified, it will default to the animate config.
65827      * @return {Ext.form.SliderField} this
65828      */
65829     setValue : function(v, animate, /* private */ silent){
65830         // silent is used if the setValue method is invoked by the slider
65831         // which means we don't need to set the value on the slider.
65832         if(!silent){
65833             this.slider.setValue(v, animate);
65834         }
65835         return Ext.form.SliderField.superclass.setValue.call(this, this.slider.getValue());
65836     },
65837     
65838     /**
65839      * Gets the current value for this field.
65840      * @return {Number} The current value.
65841      */
65842     getValue : function(){
65843         return this.slider.getValue();    
65844     }
65845 });
65846
65847 Ext.reg('sliderfield', Ext.form.SliderField);/**
65848  * @class Ext.form.Label
65849  * @extends Ext.BoxComponent
65850  * Basic Label field.
65851  * @constructor
65852  * Creates a new Label
65853  * @param {Ext.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
65854  * 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
65855  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
65856  * @xtype label
65857  */
65858 Ext.form.Label = Ext.extend(Ext.BoxComponent, {
65859     /**
65860      * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
65861      * tags within the label's innerHTML, use the {@link #html} config instead.
65862      */
65863     /**
65864      * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
65865      * attribute. If not specified, the attribute will not be added to the label.
65866      */
65867     /**
65868      * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
65869      * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
65870      */
65871
65872     // private
65873     onRender : function(ct, position){
65874         if(!this.el){
65875             this.el = document.createElement('label');
65876             this.el.id = this.getId();
65877             this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
65878             if(this.forId){
65879                 this.el.setAttribute('for', this.forId);
65880             }
65881         }
65882         Ext.form.Label.superclass.onRender.call(this, ct, position);
65883     },
65884
65885     /**
65886      * Updates the label's innerHTML with the specified string.
65887      * @param {String} text The new label text
65888      * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
65889      * to the label (defaults to true which encodes the value). This might be useful if you want to include
65890      * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
65891      * @return {Label} this
65892      */
65893     setText : function(t, encode){
65894         var e = encode === false;
65895         this[!e ? 'text' : 'html'] = t;
65896         delete this[e ? 'text' : 'html'];
65897         if(this.rendered){
65898             this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
65899         }
65900         return this;
65901     }
65902 });
65903
65904 Ext.reg('label', Ext.form.Label);/**
65905  * @class Ext.form.Action
65906  * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.BasicForm Form}s.</p>
65907  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
65908  * the Form needs to perform an action such as submit or load. The Configuration options
65909  * listed for this class are set through the Form's action methods: {@link Ext.form.BasicForm#submit submit},
65910  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}</p>
65911  * <p>The instance of Action which performed the action is passed to the success
65912  * and failure callbacks of the Form's action methods ({@link Ext.form.BasicForm#submit submit},
65913  * {@link Ext.form.BasicForm#load load} and {@link Ext.form.BasicForm#doAction doAction}),
65914  * and to the {@link Ext.form.BasicForm#actioncomplete actioncomplete} and
65915  * {@link Ext.form.BasicForm#actionfailed actionfailed} event handlers.</p>
65916  */
65917 Ext.form.Action = function(form, options){
65918     this.form = form;
65919     this.options = options || {};
65920 };
65921
65922 /**
65923  * Failure type returned when client side validation of the Form fails
65924  * thus aborting a submit action. Client side validation is performed unless
65925  * {@link #clientValidation} is explicitly set to <tt>false</tt>.
65926  * @type {String}
65927  * @static
65928  */
65929 Ext.form.Action.CLIENT_INVALID = 'client';
65930 /**
65931  * <p>Failure type returned when server side processing fails and the {@link #result}'s
65932  * <tt style="font-weight:bold">success</tt> property is set to <tt>false</tt>.</p>
65933  * <p>In the case of a form submission, field-specific error messages may be returned in the
65934  * {@link #result}'s <tt style="font-weight:bold">errors</tt> property.</p>
65935  * @type {String}
65936  * @static
65937  */
65938 Ext.form.Action.SERVER_INVALID = 'server';
65939 /**
65940  * Failure type returned when a communication error happens when attempting
65941  * to send a request to the remote server. The {@link #response} may be examined to
65942  * provide further information.
65943  * @type {String}
65944  * @static
65945  */
65946 Ext.form.Action.CONNECT_FAILURE = 'connect';
65947 /**
65948  * Failure type returned when the response's <tt style="font-weight:bold">success</tt>
65949  * property is set to <tt>false</tt>, or no field values are returned in the response's
65950  * <tt style="font-weight:bold">data</tt> property.
65951  * @type {String}
65952  * @static
65953  */
65954 Ext.form.Action.LOAD_FAILURE = 'load';
65955
65956 Ext.form.Action.prototype = {
65957 /**
65958  * @cfg {String} url The URL that the Action is to invoke.
65959  */
65960 /**
65961  * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
65962  * {@link Ext.form.BasicForm.reset reset} on Action success. If specified, this happens
65963  * <b>before</b> the {@link #success} callback is called and before the Form's
65964  * {@link Ext.form.BasicForm.actioncomplete actioncomplete} event fires.
65965  */
65966 /**
65967  * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
65968  * {@link Ext.form.BasicForm}'s method, or if that is not specified, the underlying DOM form's method.
65969  */
65970 /**
65971  * @cfg {Mixed} params <p>Extra parameter values to pass. These are added to the Form's
65972  * {@link Ext.form.BasicForm#baseParams} and passed to the specified URL along with the Form's
65973  * input fields.</p>
65974  * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p>
65975  */
65976 /**
65977  * @cfg {Number} timeout The number of seconds to wait for a server response before
65978  * failing with the {@link #failureType} as {@link #Action.CONNECT_FAILURE}. If not specified,
65979  * defaults to the configured <tt>{@link Ext.form.BasicForm#timeout timeout}</tt> of the
65980  * {@link Ext.form.BasicForm form}.
65981  */
65982 /**
65983  * @cfg {Function} success The function to call when a valid success return packet is recieved.
65984  * The function is passed the following parameters:<ul class="mdetail-params">
65985  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
65986  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. The {@link #result}
65987  * property of this object may be examined to perform custom postprocessing.</div></li>
65988  * </ul>
65989  */
65990 /**
65991  * @cfg {Function} failure The function to call when a failure packet was recieved, or when an
65992  * error ocurred in the Ajax communication.
65993  * The function is passed the following parameters:<ul class="mdetail-params">
65994  * <li><b>form</b> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
65995  * <li><b>action</b> : Ext.form.Action<div class="sub-desc">The Action class. If an Ajax
65996  * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
65997  * property of this object may be examined to perform custom postprocessing.</div></li>
65998  * </ul>
65999  */
66000 /**
66001  * @cfg {Object} scope The scope in which to call the callback functions (The <tt>this</tt> reference
66002  * for the callback functions).
66003  */
66004 /**
66005  * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.MessageBox#wait}
66006  * during the time the action is being processed.
66007  */
66008 /**
66009  * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.MessageBox#wait}
66010  * during the time the action is being processed.
66011  */
66012
66013 /**
66014  * @cfg {Boolean} submitEmptyText If set to <tt>true</tt>, the emptyText value will be sent with the form
66015  * when it is submitted.  Defaults to <tt>true</tt>.
66016  */
66017
66018 /**
66019  * The type of action this Action instance performs.
66020  * Currently only "submit" and "load" are supported.
66021  * @type {String}
66022  */
66023     type : 'default',
66024 /**
66025  * The type of failure detected will be one of these: {@link #CLIENT_INVALID},
66026  * {@link #SERVER_INVALID}, {@link #CONNECT_FAILURE}, or {@link #LOAD_FAILURE}.  Usage:
66027  * <pre><code>
66028 var fp = new Ext.form.FormPanel({
66029 ...
66030 buttons: [{
66031     text: 'Save',
66032     formBind: true,
66033     handler: function(){
66034         if(fp.getForm().isValid()){
66035             fp.getForm().submit({
66036                 url: 'form-submit.php',
66037                 waitMsg: 'Submitting your data...',
66038                 success: function(form, action){
66039                     // server responded with success = true
66040                     var result = action.{@link #result};
66041                 },
66042                 failure: function(form, action){
66043                     if (action.{@link #failureType} === Ext.form.Action.{@link #CONNECT_FAILURE}) {
66044                         Ext.Msg.alert('Error',
66045                             'Status:'+action.{@link #response}.status+': '+
66046                             action.{@link #response}.statusText);
66047                     }
66048                     if (action.failureType === Ext.form.Action.{@link #SERVER_INVALID}){
66049                         // server responded with success = false
66050                         Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
66051                     }
66052                 }
66053             });
66054         }
66055     }
66056 },{
66057     text: 'Reset',
66058     handler: function(){
66059         fp.getForm().reset();
66060     }
66061 }]
66062  * </code></pre>
66063  * @property failureType
66064  * @type {String}
66065  */
66066  /**
66067  * The XMLHttpRequest object used to perform the action.
66068  * @property response
66069  * @type {Object}
66070  */
66071  /**
66072  * The decoded response object containing a boolean <tt style="font-weight:bold">success</tt> property and
66073  * other, action-specific properties.
66074  * @property result
66075  * @type {Object}
66076  */
66077
66078     // interface method
66079     run : function(options){
66080
66081     },
66082
66083     // interface method
66084     success : function(response){
66085
66086     },
66087
66088     // interface method
66089     handleResponse : function(response){
66090
66091     },
66092
66093     // default connection failure
66094     failure : function(response){
66095         this.response = response;
66096         this.failureType = Ext.form.Action.CONNECT_FAILURE;
66097         this.form.afterAction(this, false);
66098     },
66099
66100     // private
66101     // shared code among all Actions to validate that there was a response
66102     // with either responseText or responseXml
66103     processResponse : function(response){
66104         this.response = response;
66105         if(!response.responseText && !response.responseXML){
66106             return true;
66107         }
66108         this.result = this.handleResponse(response);
66109         return this.result;
66110     },
66111
66112     // utility functions used internally
66113     getUrl : function(appendParams){
66114         var url = this.options.url || this.form.url || this.form.el.dom.action;
66115         if(appendParams){
66116             var p = this.getParams();
66117             if(p){
66118                 url = Ext.urlAppend(url, p);
66119             }
66120         }
66121         return url;
66122     },
66123
66124     // private
66125     getMethod : function(){
66126         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
66127     },
66128
66129     // private
66130     getParams : function(){
66131         var bp = this.form.baseParams;
66132         var p = this.options.params;
66133         if(p){
66134             if(typeof p == "object"){
66135                 p = Ext.urlEncode(Ext.applyIf(p, bp));
66136             }else if(typeof p == 'string' && bp){
66137                 p += '&' + Ext.urlEncode(bp);
66138             }
66139         }else if(bp){
66140             p = Ext.urlEncode(bp);
66141         }
66142         return p;
66143     },
66144
66145     // private
66146     createCallback : function(opts){
66147         var opts = opts || {};
66148         return {
66149             success: this.success,
66150             failure: this.failure,
66151             scope: this,
66152             timeout: (opts.timeout*1000) || (this.form.timeout*1000),
66153             upload: this.form.fileUpload ? this.success : undefined
66154         };
66155     }
66156 };
66157
66158 /**
66159  * @class Ext.form.Action.Submit
66160  * @extends Ext.form.Action
66161  * <p>A class which handles submission of data from {@link Ext.form.BasicForm Form}s
66162  * and processes the returned response.</p>
66163  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
66164  * {@link Ext.form.BasicForm#submit submit}ting.</p>
66165  * <p><u><b>Response Packet Criteria</b></u></p>
66166  * <p>A response packet may contain:
66167  * <div class="mdetail-params"><ul>
66168  * <li><b><code>success</code></b> property : Boolean
66169  * <div class="sub-desc">The <code>success</code> property is required.</div></li>
66170  * <li><b><code>errors</code></b> property : Object
66171  * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
66172  * which is optional, contains error messages for invalid fields.</div></li>
66173  * </ul></div>
66174  * <p><u><b>JSON Packets</b></u></p>
66175  * <p>By default, response packets are assumed to be JSON, so a typical response
66176  * packet may look like this:</p><pre><code>
66177 {
66178     success: false,
66179     errors: {
66180         clientCode: "Client not found",
66181         portOfLoading: "This field must not be null"
66182     }
66183 }</code></pre>
66184  * <p>Other data may be placed into the response for processing by the {@link Ext.form.BasicForm}'s callback
66185  * or event handler methods. The object decoded from this JSON is available in the
66186  * {@link Ext.form.Action#result result} property.</p>
66187  * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.XmlReader XmlReader}:</p><pre><code>
66188     errorReader: new Ext.data.XmlReader({
66189             record : 'field',
66190             success: '@success'
66191         }, [
66192             'id', 'msg'
66193         ]
66194     )
66195 </code></pre>
66196  * <p>then the results may be sent back in XML format:</p><pre><code>
66197 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
66198 &lt;message success="false"&gt;
66199 &lt;errors&gt;
66200     &lt;field&gt;
66201         &lt;id&gt;clientCode&lt;/id&gt;
66202         &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;
66203     &lt;/field&gt;
66204     &lt;field&gt;
66205         &lt;id&gt;portOfLoading&lt;/id&gt;
66206         &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;
66207     &lt;/field&gt;
66208 &lt;/errors&gt;
66209 &lt;/message&gt;
66210 </code></pre>
66211  * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.BasicForm}'s callback
66212  * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.XmlReader#xmlData xmlData} property.</p>
66213  */
66214 Ext.form.Action.Submit = function(form, options){
66215     Ext.form.Action.Submit.superclass.constructor.call(this, form, options);
66216 };
66217
66218 Ext.extend(Ext.form.Action.Submit, Ext.form.Action, {
66219     /**
66220      * @cfg {Ext.data.DataReader} errorReader <p><b>Optional. JSON is interpreted with
66221      * no need for an errorReader.</b></p>
66222      * <p>A Reader which reads a single record from the returned data. The DataReader's
66223      * <b>success</b> property specifies how submission success is determined. The Record's
66224      * data provides the error messages to apply to any invalid form Fields.</p>
66225      */
66226     /**
66227      * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
66228      * in a final call to {@link Ext.form.BasicForm#isValid isValid} prior to submission.
66229      * Pass <tt>false</tt> in the Form's submit options to prevent this. If not defined, pre-submission field validation
66230      * is performed.
66231      */
66232     type : 'submit',
66233
66234     // private
66235     run : function(){
66236         var o = this.options,
66237             method = this.getMethod(),
66238             isGet = method == 'GET';
66239         if(o.clientValidation === false || this.form.isValid()){
66240             if (o.submitEmptyText === false) {
66241                 var fields = this.form.items,
66242                     emptyFields = [];
66243                 fields.each(function(f) {
66244                     if (f.el.getValue() == f.emptyText) {
66245                         emptyFields.push(f);
66246                         f.el.dom.value = "";
66247                     }
66248                 });
66249             }
66250             Ext.Ajax.request(Ext.apply(this.createCallback(o), {
66251                 form:this.form.el.dom,
66252                 url:this.getUrl(isGet),
66253                 method: method,
66254                 headers: o.headers,
66255                 params:!isGet ? this.getParams() : null,
66256                 isUpload: this.form.fileUpload
66257             }));
66258             if (o.submitEmptyText === false) {
66259                 Ext.each(emptyFields, function(f) {
66260                     if (f.applyEmptyText) {
66261                         f.applyEmptyText();
66262                     }
66263                 });
66264             }
66265         }else if (o.clientValidation !== false){ // client validation failed
66266             this.failureType = Ext.form.Action.CLIENT_INVALID;
66267             this.form.afterAction(this, false);
66268         }
66269     },
66270
66271     // private
66272     success : function(response){
66273         var result = this.processResponse(response);
66274         if(result === true || result.success){
66275             this.form.afterAction(this, true);
66276             return;
66277         }
66278         if(result.errors){
66279             this.form.markInvalid(result.errors);
66280         }
66281         this.failureType = Ext.form.Action.SERVER_INVALID;
66282         this.form.afterAction(this, false);
66283     },
66284
66285     // private
66286     handleResponse : function(response){
66287         if(this.form.errorReader){
66288             var rs = this.form.errorReader.read(response);
66289             var errors = [];
66290             if(rs.records){
66291                 for(var i = 0, len = rs.records.length; i < len; i++) {
66292                     var r = rs.records[i];
66293                     errors[i] = r.data;
66294                 }
66295             }
66296             if(errors.length < 1){
66297                 errors = null;
66298             }
66299             return {
66300                 success : rs.success,
66301                 errors : errors
66302             };
66303         }
66304         return Ext.decode(response.responseText);
66305     }
66306 });
66307
66308
66309 /**
66310  * @class Ext.form.Action.Load
66311  * @extends Ext.form.Action
66312  * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.BasicForm}.</p>
66313  * <p>Instances of this class are only created by a {@link Ext.form.BasicForm Form} when
66314  * {@link Ext.form.BasicForm#load load}ing.</p>
66315  * <p><u><b>Response Packet Criteria</b></u></p>
66316  * <p>A response packet <b>must</b> contain:
66317  * <div class="mdetail-params"><ul>
66318  * <li><b><code>success</code></b> property : Boolean</li>
66319  * <li><b><code>data</code></b> property : Object</li>
66320  * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
66321  * The individual value object for each Field is passed to the Field's
66322  * {@link Ext.form.Field#setValue setValue} method.</div></li>
66323  * </ul></div>
66324  * <p><u><b>JSON Packets</b></u></p>
66325  * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
66326 var myFormPanel = new Ext.form.FormPanel({
66327     title: 'Client and routing info',
66328     items: [{
66329         fieldLabel: 'Client',
66330         name: 'clientName'
66331     }, {
66332         fieldLabel: 'Port of loading',
66333         name: 'portOfLoading'
66334     }, {
66335         fieldLabel: 'Port of discharge',
66336         name: 'portOfDischarge'
66337     }]
66338 });
66339 myFormPanel.{@link Ext.form.FormPanel#getForm getForm}().{@link Ext.form.BasicForm#load load}({
66340     url: '/getRoutingInfo.php',
66341     params: {
66342         consignmentRef: myConsignmentRef
66343     },
66344     failure: function(form, action) {
66345         Ext.Msg.alert("Load failed", action.result.errorMessage);
66346     }
66347 });
66348 </code></pre>
66349  * a <b>success response</b> packet may look like this:</p><pre><code>
66350 {
66351     success: true,
66352     data: {
66353         clientName: "Fred. Olsen Lines",
66354         portOfLoading: "FXT",
66355         portOfDischarge: "OSL"
66356     }
66357 }</code></pre>
66358  * while a <b>failure response</b> packet may look like this:</p><pre><code>
66359 {
66360     success: false,
66361     errorMessage: "Consignment reference not found"
66362 }</code></pre>
66363  * <p>Other data may be placed into the response for processing the {@link Ext.form.BasicForm Form}'s
66364  * callback or event handler methods. The object decoded from this JSON is available in the
66365  * {@link Ext.form.Action#result result} property.</p>
66366  */
66367 Ext.form.Action.Load = function(form, options){
66368     Ext.form.Action.Load.superclass.constructor.call(this, form, options);
66369     this.reader = this.form.reader;
66370 };
66371
66372 Ext.extend(Ext.form.Action.Load, Ext.form.Action, {
66373     // private
66374     type : 'load',
66375
66376     // private
66377     run : function(){
66378         Ext.Ajax.request(Ext.apply(
66379                 this.createCallback(this.options), {
66380                     method:this.getMethod(),
66381                     url:this.getUrl(false),
66382                     headers: this.options.headers,
66383                     params:this.getParams()
66384         }));
66385     },
66386
66387     // private
66388     success : function(response){
66389         var result = this.processResponse(response);
66390         if(result === true || !result.success || !result.data){
66391             this.failureType = Ext.form.Action.LOAD_FAILURE;
66392             this.form.afterAction(this, false);
66393             return;
66394         }
66395         this.form.clearInvalid();
66396         this.form.setValues(result.data);
66397         this.form.afterAction(this, true);
66398     },
66399
66400     // private
66401     handleResponse : function(response){
66402         if(this.form.reader){
66403             var rs = this.form.reader.read(response);
66404             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
66405             return {
66406                 success : rs.success,
66407                 data : data
66408             };
66409         }
66410         return Ext.decode(response.responseText);
66411     }
66412 });
66413
66414
66415
66416 /**
66417  * @class Ext.form.Action.DirectLoad
66418  * @extends Ext.form.Action.Load
66419  * <p>Provides Ext.direct support for loading form data.</p>
66420  * <p>This example illustrates usage of Ext.Direct to <b>load</b> a form through Ext.Direct.</p>
66421  * <pre><code>
66422 var myFormPanel = new Ext.form.FormPanel({
66423     // configs for FormPanel
66424     title: 'Basic Information',
66425     renderTo: document.body,
66426     width: 300, height: 160,
66427     padding: 10,
66428
66429     // configs apply to child items
66430     defaults: {anchor: '100%'},
66431     defaultType: 'textfield',
66432     items: [{
66433         fieldLabel: 'Name',
66434         name: 'name'
66435     },{
66436         fieldLabel: 'Email',
66437         name: 'email'
66438     },{
66439         fieldLabel: 'Company',
66440         name: 'company'
66441     }],
66442
66443     // configs for BasicForm
66444     api: {
66445         // The server-side method to call for load() requests
66446         load: Profile.getBasicInfo,
66447         // The server-side must mark the submit handler as a 'formHandler'
66448         submit: Profile.updateBasicInfo
66449     },
66450     // specify the order for the passed params
66451     paramOrder: ['uid', 'foo']
66452 });
66453
66454 // load the form
66455 myFormPanel.getForm().load({
66456     // pass 2 arguments to server side getBasicInfo method (len=2)
66457     params: {
66458         foo: 'bar',
66459         uid: 34
66460     }
66461 });
66462  * </code></pre>
66463  * The data packet sent to the server will resemble something like:
66464  * <pre><code>
66465 [
66466     {
66467         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
66468         "data":[34,"bar"] // note the order of the params
66469     }
66470 ]
66471  * </code></pre>
66472  * The form will process a data packet returned by the server that is similar
66473  * to the following format:
66474  * <pre><code>
66475 [
66476     {
66477         "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
66478         "result":{
66479             "success":true,
66480             "data":{
66481                 "name":"Fred Flintstone",
66482                 "company":"Slate Rock and Gravel",
66483                 "email":"fred.flintstone@slaterg.com"
66484             }
66485         }
66486     }
66487 ]
66488  * </code></pre>
66489  */
66490 Ext.form.Action.DirectLoad = Ext.extend(Ext.form.Action.Load, {
66491     constructor: function(form, opts) {
66492         Ext.form.Action.DirectLoad.superclass.constructor.call(this, form, opts);
66493     },
66494     type : 'directload',
66495
66496     run : function(){
66497         var args = this.getParams();
66498         args.push(this.success, this);
66499         this.form.api.load.apply(window, args);
66500     },
66501
66502     getParams : function() {
66503         var buf = [], o = {};
66504         var bp = this.form.baseParams;
66505         var p = this.options.params;
66506         Ext.apply(o, p, bp);
66507         var paramOrder = this.form.paramOrder;
66508         if(paramOrder){
66509             for(var i = 0, len = paramOrder.length; i < len; i++){
66510                 buf.push(o[paramOrder[i]]);
66511             }
66512         }else if(this.form.paramsAsHash){
66513             buf.push(o);
66514         }
66515         return buf;
66516     },
66517     // Direct actions have already been processed and therefore
66518     // we can directly set the result; Direct Actions do not have
66519     // a this.response property.
66520     processResponse : function(result) {
66521         this.result = result;
66522         return result;
66523     },
66524
66525     success : function(response, trans){
66526         if(trans.type == Ext.Direct.exceptions.SERVER){
66527             response = {};
66528         }
66529         Ext.form.Action.DirectLoad.superclass.success.call(this, response);
66530     }
66531 });
66532
66533 /**
66534  * @class Ext.form.Action.DirectSubmit
66535  * @extends Ext.form.Action.Submit
66536  * <p>Provides Ext.direct support for submitting form data.</p>
66537  * <p>This example illustrates usage of Ext.Direct to <b>submit</b> a form through Ext.Direct.</p>
66538  * <pre><code>
66539 var myFormPanel = new Ext.form.FormPanel({
66540     // configs for FormPanel
66541     title: 'Basic Information',
66542     renderTo: document.body,
66543     width: 300, height: 160,
66544     padding: 10,
66545     buttons:[{
66546         text: 'Submit',
66547         handler: function(){
66548             myFormPanel.getForm().submit({
66549                 params: {
66550                     foo: 'bar',
66551                     uid: 34
66552                 }
66553             });
66554         }
66555     }],
66556
66557     // configs apply to child items
66558     defaults: {anchor: '100%'},
66559     defaultType: 'textfield',
66560     items: [{
66561         fieldLabel: 'Name',
66562         name: 'name'
66563     },{
66564         fieldLabel: 'Email',
66565         name: 'email'
66566     },{
66567         fieldLabel: 'Company',
66568         name: 'company'
66569     }],
66570
66571     // configs for BasicForm
66572     api: {
66573         // The server-side method to call for load() requests
66574         load: Profile.getBasicInfo,
66575         // The server-side must mark the submit handler as a 'formHandler'
66576         submit: Profile.updateBasicInfo
66577     },
66578     // specify the order for the passed params
66579     paramOrder: ['uid', 'foo']
66580 });
66581  * </code></pre>
66582  * The data packet sent to the server will resemble something like:
66583  * <pre><code>
66584 {
66585     "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
66586     "result":{
66587         "success":true,
66588         "id":{
66589             "extAction":"Profile","extMethod":"updateBasicInfo",
66590             "extType":"rpc","extTID":"6","extUpload":"false",
66591             "name":"Aaron Conran","email":"aaron@extjs.com","company":"Ext JS, LLC"
66592         }
66593     }
66594 }
66595  * </code></pre>
66596  * The form will process a data packet returned by the server that is similar
66597  * to the following:
66598  * <pre><code>
66599 // sample success packet (batched requests)
66600 [
66601     {
66602         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
66603         "result":{
66604             "success":true
66605         }
66606     }
66607 ]
66608
66609 // sample failure packet (one request)
66610 {
66611         "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
66612         "result":{
66613             "errors":{
66614                 "email":"already taken"
66615             },
66616             "success":false,
66617             "foo":"bar"
66618         }
66619 }
66620  * </code></pre>
66621  * Also see the discussion in {@link Ext.form.Action.DirectLoad}.
66622  */
66623 Ext.form.Action.DirectSubmit = Ext.extend(Ext.form.Action.Submit, {
66624     constructor : function(form, opts) {
66625         Ext.form.Action.DirectSubmit.superclass.constructor.call(this, form, opts);
66626     },
66627     type : 'directsubmit',
66628     // override of Submit
66629     run : function(){
66630         var o = this.options;
66631         if(o.clientValidation === false || this.form.isValid()){
66632             // tag on any additional params to be posted in the
66633             // form scope
66634             this.success.params = this.getParams();
66635             this.form.api.submit(this.form.el.dom, this.success, this);
66636         }else if (o.clientValidation !== false){ // client validation failed
66637             this.failureType = Ext.form.Action.CLIENT_INVALID;
66638             this.form.afterAction(this, false);
66639         }
66640     },
66641
66642     getParams : function() {
66643         var o = {};
66644         var bp = this.form.baseParams;
66645         var p = this.options.params;
66646         Ext.apply(o, p, bp);
66647         return o;
66648     },
66649     // Direct actions have already been processed and therefore
66650     // we can directly set the result; Direct Actions do not have
66651     // a this.response property.
66652     processResponse : function(result) {
66653         this.result = result;
66654         return result;
66655     },
66656
66657     success : function(response, trans){
66658         if(trans.type == Ext.Direct.exceptions.SERVER){
66659             response = {};
66660         }
66661         Ext.form.Action.DirectSubmit.superclass.success.call(this, response);
66662     }
66663 });
66664
66665 Ext.form.Action.ACTION_TYPES = {
66666     'load' : Ext.form.Action.Load,
66667     'submit' : Ext.form.Action.Submit,
66668     'directload' : Ext.form.Action.DirectLoad,
66669     'directsubmit' : Ext.form.Action.DirectSubmit
66670 };
66671 /**
66672  * @class Ext.form.VTypes
66673  * <p>This is a singleton object which contains a set of commonly used field validation functions.
66674  * The validations provided are basic and intended to be easily customizable and extended.</p>
66675  * <p>To add custom VTypes specify the <code>{@link Ext.form.TextField#vtype vtype}</code> validation
66676  * test function, and optionally specify any corresponding error text to display and any keystroke
66677  * filtering mask to apply. For example:</p>
66678  * <pre><code>
66679 // custom Vtype for vtype:'time'
66680 var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
66681 Ext.apply(Ext.form.VTypes, {
66682     //  vtype validation function
66683     time: function(val, field) {
66684         return timeTest.test(val);
66685     },
66686     // vtype Text property: The error text to display when the validation function returns false
66687     timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
66688     // vtype Mask property: The keystroke filter mask
66689     timeMask: /[\d\s:amp]/i
66690 });
66691  * </code></pre>
66692  * Another example:
66693  * <pre><code>
66694 // custom Vtype for vtype:'IPAddress'
66695 Ext.apply(Ext.form.VTypes, {
66696     IPAddress:  function(v) {
66697         return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
66698     },
66699     IPAddressText: 'Must be a numeric IP address',
66700     IPAddressMask: /[\d\.]/i
66701 });
66702  * </code></pre>
66703  * @singleton
66704  */
66705 Ext.form.VTypes = function(){
66706     // closure these in so they are only created once.
66707     var alpha = /^[a-zA-Z_]+$/,
66708         alphanum = /^[a-zA-Z0-9_]+$/,
66709         email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
66710         url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
66711
66712     // All these messages and functions are configurable
66713     return {
66714         /**
66715          * The function used to validate email addresses.  Note that this is a very basic validation -- complete
66716          * validation per the email RFC specifications is very complex and beyond the scope of this class, although
66717          * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
66718          * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a>
66719          * for additional information.  This implementation is intended to validate the following emails:<tt>
66720          * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
66721          * </tt>.
66722          * @param {String} value The email address
66723          * @return {Boolean} true if the RegExp test passed, and false if not.
66724          */
66725         'email' : function(v){
66726             return email.test(v);
66727         },
66728         /**
66729          * The error text to display when the email validation function returns false.  Defaults to:
66730          * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
66731          * @type String
66732          */
66733         'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
66734         /**
66735          * The keystroke filter mask to be applied on email input.  See the {@link #email} method for
66736          * information about more complex email validation. Defaults to:
66737          * <tt>/[a-z0-9_\.\-@]/i</tt>
66738          * @type RegExp
66739          */
66740         'emailMask' : /[a-z0-9_\.\-@\+]/i,
66741
66742         /**
66743          * The function used to validate URLs
66744          * @param {String} value The URL
66745          * @return {Boolean} true if the RegExp test passed, and false if not.
66746          */
66747         'url' : function(v){
66748             return url.test(v);
66749         },
66750         /**
66751          * The error text to display when the url validation function returns false.  Defaults to:
66752          * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
66753          * @type String
66754          */
66755         'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
66756
66757         /**
66758          * The function used to validate alpha values
66759          * @param {String} value The value
66760          * @return {Boolean} true if the RegExp test passed, and false if not.
66761          */
66762         'alpha' : function(v){
66763             return alpha.test(v);
66764         },
66765         /**
66766          * The error text to display when the alpha validation function returns false.  Defaults to:
66767          * <tt>'This field should only contain letters and _'</tt>
66768          * @type String
66769          */
66770         'alphaText' : 'This field should only contain letters and _',
66771         /**
66772          * The keystroke filter mask to be applied on alpha input.  Defaults to:
66773          * <tt>/[a-z_]/i</tt>
66774          * @type RegExp
66775          */
66776         'alphaMask' : /[a-z_]/i,
66777
66778         /**
66779          * The function used to validate alphanumeric values
66780          * @param {String} value The value
66781          * @return {Boolean} true if the RegExp test passed, and false if not.
66782          */
66783         'alphanum' : function(v){
66784             return alphanum.test(v);
66785         },
66786         /**
66787          * The error text to display when the alphanumeric validation function returns false.  Defaults to:
66788          * <tt>'This field should only contain letters, numbers and _'</tt>
66789          * @type String
66790          */
66791         'alphanumText' : 'This field should only contain letters, numbers and _',
66792         /**
66793          * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
66794          * <tt>/[a-z0-9_]/i</tt>
66795          * @type RegExp
66796          */
66797         'alphanumMask' : /[a-z0-9_]/i
66798     };
66799 }();
66800 /**
66801  * @class Ext.grid.GridPanel
66802  * @extends Ext.Panel
66803  * <p>This class represents the primary interface of a component based grid control to represent data
66804  * in a tabular format of rows and columns. The GridPanel is composed of the following:</p>
66805  * <div class="mdetail-params"><ul>
66806  * <li><b>{@link Ext.data.Store Store}</b> : The Model holding the data records (rows)
66807  * <div class="sub-desc"></div></li>
66808  * <li><b>{@link Ext.grid.ColumnModel Column model}</b> : Column makeup
66809  * <div class="sub-desc"></div></li>
66810  * <li><b>{@link Ext.grid.GridView View}</b> : Encapsulates the user interface
66811  * <div class="sub-desc"></div></li>
66812  * <li><b>{@link Ext.grid.AbstractSelectionModel selection model}</b> : Selection behavior
66813  * <div class="sub-desc"></div></li>
66814  * </ul></div>
66815  * <p>Example usage:</p>
66816  * <pre><code>
66817 var grid = new Ext.grid.GridPanel({
66818     {@link #store}: new {@link Ext.data.Store}({
66819         {@link Ext.data.Store#autoDestroy autoDestroy}: true,
66820         {@link Ext.data.Store#reader reader}: reader,
66821         {@link Ext.data.Store#data data}: xg.dummyData
66822     }),
66823     {@link #colModel}: new {@link Ext.grid.ColumnModel}({
66824         {@link Ext.grid.ColumnModel#defaults defaults}: {
66825             width: 120,
66826             sortable: true
66827         },
66828         {@link Ext.grid.ColumnModel#columns columns}: [
66829             {id: 'company', header: 'Company', width: 200, sortable: true, dataIndex: 'company'},
66830             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
66831             {header: 'Change', dataIndex: 'change'},
66832             {header: '% Change', dataIndex: 'pctChange'},
66833             // instead of specifying renderer: Ext.util.Format.dateRenderer('m/d/Y') use xtype
66834             {
66835                 header: 'Last Updated', width: 135, dataIndex: 'lastChange',
66836                 xtype: 'datecolumn', format: 'M d, Y'
66837             }
66838         ],
66839     }),
66840     {@link #viewConfig}: {
66841         {@link Ext.grid.GridView#forceFit forceFit}: true,
66842
66843 //      Return CSS class to apply to rows depending upon data values
66844         {@link Ext.grid.GridView#getRowClass getRowClass}: function(record, index) {
66845             var c = record.{@link Ext.data.Record#get get}('change');
66846             if (c < 0) {
66847                 return 'price-fall';
66848             } else if (c > 0) {
66849                 return 'price-rise';
66850             }
66851         }
66852     },
66853     {@link #sm}: new Ext.grid.RowSelectionModel({singleSelect:true}),
66854     width: 600,
66855     height: 300,
66856     frame: true,
66857     title: 'Framed with Row Selection and Horizontal Scrolling',
66858     iconCls: 'icon-grid'
66859 });
66860  * </code></pre>
66861  * <p><b><u>Notes:</u></b></p>
66862  * <div class="mdetail-params"><ul>
66863  * <li>Although this class inherits many configuration options from base classes, some of them
66864  * (such as autoScroll, autoWidth, layout, items, etc) are not used by this class, and will
66865  * have no effect.</li>
66866  * <li>A grid <b>requires</b> a width in which to scroll its columns, and a height in which to
66867  * scroll its rows. These dimensions can either be set explicitly through the
66868  * <tt>{@link Ext.BoxComponent#height height}</tt> and <tt>{@link Ext.BoxComponent#width width}</tt>
66869  * configuration options or implicitly set by using the grid as a child item of a
66870  * {@link Ext.Container Container} which will have a {@link Ext.Container#layout layout manager}
66871  * provide the sizing of its child items (for example the Container of the Grid may specify
66872  * <tt>{@link Ext.Container#layout layout}:'fit'</tt>).</li>
66873  * <li>To access the data in a Grid, it is necessary to use the data model encapsulated
66874  * by the {@link #store Store}. See the {@link #cellclick} event for more details.</li>
66875  * </ul></div>
66876  * @constructor
66877  * @param {Object} config The config object
66878  * @xtype grid
66879  */
66880 Ext.grid.GridPanel = Ext.extend(Ext.Panel, {
66881     /**
66882      * @cfg {String} autoExpandColumn
66883      * <p>The <tt>{@link Ext.grid.Column#id id}</tt> of a {@link Ext.grid.Column column} in
66884      * this grid that should expand to fill unused space. This value specified here can not
66885      * be <tt>0</tt>.</p>
66886      * <br><p><b>Note</b>: If the Grid's {@link Ext.grid.GridView view} is configured with
66887      * <tt>{@link Ext.grid.GridView#forceFit forceFit}=true</tt> the <tt>autoExpandColumn</tt>
66888      * is ignored. See {@link Ext.grid.Column}.<tt>{@link Ext.grid.Column#width width}</tt>
66889      * for additional details.</p>
66890      * <p>See <tt>{@link #autoExpandMax}</tt> and <tt>{@link #autoExpandMin}</tt> also.</p>
66891      */
66892     autoExpandColumn : false,
66893     /**
66894      * @cfg {Number} autoExpandMax The maximum width the <tt>{@link #autoExpandColumn}</tt>
66895      * can have (if enabled). Defaults to <tt>1000</tt>.
66896      */
66897     autoExpandMax : 1000,
66898     /**
66899      * @cfg {Number} autoExpandMin The minimum width the <tt>{@link #autoExpandColumn}</tt>
66900      * can have (if enabled). Defaults to <tt>50</tt>.
66901      */
66902     autoExpandMin : 50,
66903     /**
66904      * @cfg {Boolean} columnLines <tt>true</tt> to add css for column separation lines.
66905      * Default is <tt>false</tt>.
66906      */
66907     columnLines : false,
66908     /**
66909      * @cfg {Object} cm Shorthand for <tt>{@link #colModel}</tt>.
66910      */
66911     /**
66912      * @cfg {Object} colModel The {@link Ext.grid.ColumnModel} to use when rendering the grid (required).
66913      */
66914     /**
66915      * @cfg {Array} columns An array of {@link Ext.grid.Column columns} to auto create a
66916      * {@link Ext.grid.ColumnModel}.  The ColumnModel may be explicitly created via the
66917      * <tt>{@link #colModel}</tt> configuration property.
66918      */
66919     /**
66920      * @cfg {String} ddGroup The DD group this GridPanel belongs to. Defaults to <tt>'GridDD'</tt> if not specified.
66921      */
66922     /**
66923      * @cfg {String} ddText
66924      * Configures the text in the drag proxy.  Defaults to:
66925      * <pre><code>
66926      * ddText : '{0} selected row{1}'
66927      * </code></pre>
66928      * <tt>{0}</tt> is replaced with the number of selected rows.
66929      */
66930     ddText : '{0} selected row{1}',
66931     /**
66932      * @cfg {Boolean} deferRowRender <P>Defaults to <tt>true</tt> to enable deferred row rendering.</p>
66933      * <p>This allows the GridPanel to be initially rendered empty, with the expensive update of the row
66934      * structure deferred so that layouts with GridPanels appear more quickly.</p>
66935      */
66936     deferRowRender : true,
66937     /**
66938      * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selections in the grid. Defaults to <tt>false</tt>.</p>
66939      * <p>Ignored if a {@link #selModel SelectionModel} is specified.</p>
66940      */
66941     /**
66942      * @cfg {Boolean} enableColumnResize <tt>false</tt> to turn off column resizing for the whole grid. Defaults to <tt>true</tt>.
66943      */
66944     /**
66945      * @cfg {Boolean} enableColumnHide
66946      * Defaults to <tt>true</tt> to enable {@link Ext.grid.Column#hidden hiding of columns}
66947      * with the {@link #enableHdMenu header menu}.
66948      */
66949     enableColumnHide : true,
66950     /**
66951      * @cfg {Boolean} enableColumnMove Defaults to <tt>true</tt> to enable drag and drop reorder of columns. <tt>false</tt>
66952      * to turn off column reordering via drag drop.
66953      */
66954     enableColumnMove : true,
66955     /**
66956      * @cfg {Boolean} enableDragDrop <p>Enables dragging of the selected rows of the GridPanel. Defaults to <tt>false</tt>.</p>
66957      * <p>Setting this to <b><tt>true</tt></b> causes this GridPanel's {@link #getView GridView} to
66958      * create an instance of {@link Ext.grid.GridDragZone}. <b>Note</b>: this is available only <b>after</b>
66959      * the Grid has been rendered as the GridView's <tt>{@link Ext.grid.GridView#dragZone dragZone}</tt>
66960      * property.</p>
66961      * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's implementations of
66962      * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
66963      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} are able
66964      * to process the {@link Ext.grid.GridDragZone#getDragData data} which is provided.</p>
66965      */
66966     enableDragDrop : false,
66967     /**
66968      * @cfg {Boolean} enableHdMenu Defaults to <tt>true</tt> to enable the drop down button for menu in the headers.
66969      */
66970     enableHdMenu : true,
66971     /**
66972      * @cfg {Boolean} hideHeaders True to hide the grid's header. Defaults to <code>false</code>.
66973      */
66974     /**
66975      * @cfg {Object} loadMask An {@link Ext.LoadMask} config or true to mask the grid while
66976      * loading. Defaults to <code>false</code>.
66977      */
66978     loadMask : false,
66979     /**
66980      * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if <tt>autoHeight</tt> is not on.
66981      */
66982     /**
66983      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Defaults to <tt>25</tt>.
66984      */
66985     minColumnWidth : 25,
66986     /**
66987      * @cfg {Object} sm Shorthand for <tt>{@link #selModel}</tt>.
66988      */
66989     /**
66990      * @cfg {Object} selModel Any subclass of {@link Ext.grid.AbstractSelectionModel} that will provide
66991      * the selection model for the grid (defaults to {@link Ext.grid.RowSelectionModel} if not specified).
66992      */
66993     /**
66994      * @cfg {Ext.data.Store} store The {@link Ext.data.Store} the grid should use as its data source (required).
66995      */
66996     /**
66997      * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
66998      * <p>This causes the CSS class <tt><b>x-grid3-row-alt</b></tt> to be added to alternate rows of
66999      * the grid. A default CSS rule is provided which sets a background colour, but you can override this
67000      * with a rule which either overrides the <b>background-color</b> style using the '!important'
67001      * modifier, or which uses a CSS selector of higher specificity.</p>
67002      */
67003     stripeRows : false,
67004     /**
67005      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is <tt>true</tt>
67006      * for GridPanel, but <tt>false</tt> for EditorGridPanel.
67007      */
67008     trackMouseOver : true,
67009     /**
67010      * @cfg {Array} stateEvents
67011      * An array of events that, when fired, should trigger this component to save its state.
67012      * Defaults to:<pre><code>
67013      * stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange']
67014      * </code></pre>
67015      * <p>These can be any types of events supported by this component, including browser or
67016      * custom events (e.g., <tt>['click', 'customerchange']</tt>).</p>
67017      * <p>See {@link Ext.Component#stateful} for an explanation of saving and restoring
67018      * Component state.</p>
67019      */
67020     stateEvents : ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
67021     /**
67022      * @cfg {Object} view The {@link Ext.grid.GridView} used by the grid. This can be set
67023      * before a call to {@link Ext.Component#render render()}.
67024      */
67025     view : null,
67026
67027     /**
67028      * @cfg {Array} bubbleEvents
67029      * <p>An array of events that, when fired, should be bubbled to any parent container.
67030      * See {@link Ext.util.Observable#enableBubble}.
67031      * Defaults to <tt>[]</tt>.
67032      */
67033     bubbleEvents: [],
67034
67035     /**
67036      * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view.  Any of
67037      * the config options available for {@link Ext.grid.GridView} can be specified here. This option
67038      * is ignored if <tt>{@link #view}</tt> is specified.
67039      */
67040
67041     // private
67042     rendered : false,
67043     // private
67044     viewReady : false,
67045
67046     // private
67047     initComponent : function(){
67048         Ext.grid.GridPanel.superclass.initComponent.call(this);
67049
67050         if(this.columnLines){
67051             this.cls = (this.cls || '') + ' x-grid-with-col-lines';
67052         }
67053         // override any provided value since it isn't valid
67054         // and is causing too many bug reports ;)
67055         this.autoScroll = false;
67056         this.autoWidth = false;
67057
67058         if(Ext.isArray(this.columns)){
67059             this.colModel = new Ext.grid.ColumnModel(this.columns);
67060             delete this.columns;
67061         }
67062
67063         // check and correct shorthanded configs
67064         if(this.ds){
67065             this.store = this.ds;
67066             delete this.ds;
67067         }
67068         if(this.cm){
67069             this.colModel = this.cm;
67070             delete this.cm;
67071         }
67072         if(this.sm){
67073             this.selModel = this.sm;
67074             delete this.sm;
67075         }
67076         this.store = Ext.StoreMgr.lookup(this.store);
67077
67078         this.addEvents(
67079             // raw events
67080             /**
67081              * @event click
67082              * The raw click event for the entire grid.
67083              * @param {Ext.EventObject} e
67084              */
67085             'click',
67086             /**
67087              * @event dblclick
67088              * The raw dblclick event for the entire grid.
67089              * @param {Ext.EventObject} e
67090              */
67091             'dblclick',
67092             /**
67093              * @event contextmenu
67094              * The raw contextmenu event for the entire grid.
67095              * @param {Ext.EventObject} e
67096              */
67097             'contextmenu',
67098             /**
67099              * @event mousedown
67100              * The raw mousedown event for the entire grid.
67101              * @param {Ext.EventObject} e
67102              */
67103             'mousedown',
67104             /**
67105              * @event mouseup
67106              * The raw mouseup event for the entire grid.
67107              * @param {Ext.EventObject} e
67108              */
67109             'mouseup',
67110             /**
67111              * @event mouseover
67112              * The raw mouseover event for the entire grid.
67113              * @param {Ext.EventObject} e
67114              */
67115             'mouseover',
67116             /**
67117              * @event mouseout
67118              * The raw mouseout event for the entire grid.
67119              * @param {Ext.EventObject} e
67120              */
67121             'mouseout',
67122             /**
67123              * @event keypress
67124              * The raw keypress event for the entire grid.
67125              * @param {Ext.EventObject} e
67126              */
67127             'keypress',
67128             /**
67129              * @event keydown
67130              * The raw keydown event for the entire grid.
67131              * @param {Ext.EventObject} e
67132              */
67133             'keydown',
67134
67135             // custom events
67136             /**
67137              * @event cellmousedown
67138              * Fires before a cell is clicked
67139              * @param {Grid} this
67140              * @param {Number} rowIndex
67141              * @param {Number} columnIndex
67142              * @param {Ext.EventObject} e
67143              */
67144             'cellmousedown',
67145             /**
67146              * @event rowmousedown
67147              * Fires before a row is clicked
67148              * @param {Grid} this
67149              * @param {Number} rowIndex
67150              * @param {Ext.EventObject} e
67151              */
67152             'rowmousedown',
67153             /**
67154              * @event headermousedown
67155              * Fires before a header is clicked
67156              * @param {Grid} this
67157              * @param {Number} columnIndex
67158              * @param {Ext.EventObject} e
67159              */
67160             'headermousedown',
67161
67162             /**
67163              * @event groupmousedown
67164              * Fires before a group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67165              * @param {Grid} this
67166              * @param {String} groupField
67167              * @param {String} groupValue
67168              * @param {Ext.EventObject} e
67169              */
67170             'groupmousedown',
67171
67172             /**
67173              * @event rowbodymousedown
67174              * Fires before the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67175              * @param {Grid} this
67176              * @param {Number} rowIndex
67177              * @param {Ext.EventObject} e
67178              */
67179             'rowbodymousedown',
67180
67181             /**
67182              * @event containermousedown
67183              * Fires before the container is clicked. The container consists of any part of the grid body that is not covered by a row.
67184              * @param {Grid} this
67185              * @param {Ext.EventObject} e
67186              */
67187             'containermousedown',
67188
67189             /**
67190              * @event cellclick
67191              * Fires when a cell is clicked.
67192              * The data for the cell is drawn from the {@link Ext.data.Record Record}
67193              * for this row. To access the data in the listener function use the
67194              * following technique:
67195              * <pre><code>
67196 function(grid, rowIndex, columnIndex, e) {
67197     var record = grid.getStore().getAt(rowIndex);  // Get the Record
67198     var fieldName = grid.getColumnModel().getDataIndex(columnIndex); // Get field name
67199     var data = record.get(fieldName);
67200 }
67201 </code></pre>
67202              * @param {Grid} this
67203              * @param {Number} rowIndex
67204              * @param {Number} columnIndex
67205              * @param {Ext.EventObject} e
67206              */
67207             'cellclick',
67208             /**
67209              * @event celldblclick
67210              * Fires when a cell is double clicked
67211              * @param {Grid} this
67212              * @param {Number} rowIndex
67213              * @param {Number} columnIndex
67214              * @param {Ext.EventObject} e
67215              */
67216             'celldblclick',
67217             /**
67218              * @event rowclick
67219              * Fires when a row is clicked
67220              * @param {Grid} this
67221              * @param {Number} rowIndex
67222              * @param {Ext.EventObject} e
67223              */
67224             'rowclick',
67225             /**
67226              * @event rowdblclick
67227              * Fires when a row is double clicked
67228              * @param {Grid} this
67229              * @param {Number} rowIndex
67230              * @param {Ext.EventObject} e
67231              */
67232             'rowdblclick',
67233             /**
67234              * @event headerclick
67235              * Fires when a header is clicked
67236              * @param {Grid} this
67237              * @param {Number} columnIndex
67238              * @param {Ext.EventObject} e
67239              */
67240             'headerclick',
67241             /**
67242              * @event headerdblclick
67243              * Fires when a header cell is double clicked
67244              * @param {Grid} this
67245              * @param {Number} columnIndex
67246              * @param {Ext.EventObject} e
67247              */
67248             'headerdblclick',
67249             /**
67250              * @event groupclick
67251              * Fires when group header is clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67252              * @param {Grid} this
67253              * @param {String} groupField
67254              * @param {String} groupValue
67255              * @param {Ext.EventObject} e
67256              */
67257             'groupclick',
67258             /**
67259              * @event groupdblclick
67260              * Fires when group header is double clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67261              * @param {Grid} this
67262              * @param {String} groupField
67263              * @param {String} groupValue
67264              * @param {Ext.EventObject} e
67265              */
67266             'groupdblclick',
67267             /**
67268              * @event containerclick
67269              * Fires when the container is clicked. The container consists of any part of the grid body that is not covered by a row.
67270              * @param {Grid} this
67271              * @param {Ext.EventObject} e
67272              */
67273             'containerclick',
67274             /**
67275              * @event containerdblclick
67276              * Fires when the container is double clicked. The container consists of any part of the grid body that is not covered by a row.
67277              * @param {Grid} this
67278              * @param {Ext.EventObject} e
67279              */
67280             'containerdblclick',
67281
67282             /**
67283              * @event rowbodyclick
67284              * Fires when the row body is clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67285              * @param {Grid} this
67286              * @param {Number} rowIndex
67287              * @param {Ext.EventObject} e
67288              */
67289             'rowbodyclick',
67290             /**
67291              * @event rowbodydblclick
67292              * Fires when the row body is double clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67293              * @param {Grid} this
67294              * @param {Number} rowIndex
67295              * @param {Ext.EventObject} e
67296              */
67297             'rowbodydblclick',
67298
67299             /**
67300              * @event rowcontextmenu
67301              * Fires when a row is right clicked
67302              * @param {Grid} this
67303              * @param {Number} rowIndex
67304              * @param {Ext.EventObject} e
67305              */
67306             'rowcontextmenu',
67307             /**
67308              * @event cellcontextmenu
67309              * Fires when a cell is right clicked
67310              * @param {Grid} this
67311              * @param {Number} rowIndex
67312              * @param {Number} cellIndex
67313              * @param {Ext.EventObject} e
67314              */
67315             'cellcontextmenu',
67316             /**
67317              * @event headercontextmenu
67318              * Fires when a header is right clicked
67319              * @param {Grid} this
67320              * @param {Number} columnIndex
67321              * @param {Ext.EventObject} e
67322              */
67323             'headercontextmenu',
67324             /**
67325              * @event groupcontextmenu
67326              * Fires when group header is right clicked. <b>Only applies for grids with a {@link Ext.grid.GroupingView GroupingView}</b>.
67327              * @param {Grid} this
67328              * @param {String} groupField
67329              * @param {String} groupValue
67330              * @param {Ext.EventObject} e
67331              */
67332             'groupcontextmenu',
67333             /**
67334              * @event containercontextmenu
67335              * Fires when the container is right clicked. The container consists of any part of the grid body that is not covered by a row.
67336              * @param {Grid} this
67337              * @param {Ext.EventObject} e
67338              */
67339             'containercontextmenu',
67340             /**
67341              * @event rowbodycontextmenu
67342              * Fires when the row body is right clicked. <b>Only applies for grids with {@link Ext.grid.GridView#enableRowBody enableRowBody} configured.</b>
67343              * @param {Grid} this
67344              * @param {Number} rowIndex
67345              * @param {Ext.EventObject} e
67346              */
67347             'rowbodycontextmenu',
67348             /**
67349              * @event bodyscroll
67350              * Fires when the body element is scrolled
67351              * @param {Number} scrollLeft
67352              * @param {Number} scrollTop
67353              */
67354             'bodyscroll',
67355             /**
67356              * @event columnresize
67357              * Fires when the user resizes a column
67358              * @param {Number} columnIndex
67359              * @param {Number} newSize
67360              */
67361             'columnresize',
67362             /**
67363              * @event columnmove
67364              * Fires when the user moves a column
67365              * @param {Number} oldIndex
67366              * @param {Number} newIndex
67367              */
67368             'columnmove',
67369             /**
67370              * @event sortchange
67371              * Fires when the grid's store sort changes
67372              * @param {Grid} this
67373              * @param {Object} sortInfo An object with the keys field and direction
67374              */
67375             'sortchange',
67376             /**
67377              * @event groupchange
67378              * Fires when the grid's grouping changes (only applies for grids with a {@link Ext.grid.GroupingView GroupingView})
67379              * @param {Grid} this
67380              * @param {String} groupField A string with the grouping field, null if the store is not grouped.
67381              */
67382             'groupchange',
67383             /**
67384              * @event reconfigure
67385              * Fires when the grid is reconfigured with a new store and/or column model.
67386              * @param {Grid} this
67387              * @param {Ext.data.Store} store The new store
67388              * @param {Ext.grid.ColumnModel} colModel The new column model
67389              */
67390             'reconfigure',
67391             /**
67392              * @event viewready
67393              * Fires when the grid view is available (use this for selecting a default row).
67394              * @param {Grid} this
67395              */
67396             'viewready'
67397         );
67398     },
67399
67400     // private
67401     onRender : function(ct, position){
67402         Ext.grid.GridPanel.superclass.onRender.apply(this, arguments);
67403
67404         var c = this.getGridEl();
67405
67406         this.el.addClass('x-grid-panel');
67407
67408         this.mon(c, {
67409             scope: this,
67410             mousedown: this.onMouseDown,
67411             click: this.onClick,
67412             dblclick: this.onDblClick,
67413             contextmenu: this.onContextMenu
67414         });
67415
67416         this.relayEvents(c, ['mousedown','mouseup','mouseover','mouseout','keypress', 'keydown']);
67417
67418         var view = this.getView();
67419         view.init(this);
67420         view.render();
67421         this.getSelectionModel().init(this);
67422     },
67423
67424     // private
67425     initEvents : function(){
67426         Ext.grid.GridPanel.superclass.initEvents.call(this);
67427
67428         if(this.loadMask){
67429             this.loadMask = new Ext.LoadMask(this.bwrap,
67430                     Ext.apply({store:this.store}, this.loadMask));
67431         }
67432     },
67433
67434     initStateEvents : function(){
67435         Ext.grid.GridPanel.superclass.initStateEvents.call(this);
67436         this.mon(this.colModel, 'hiddenchange', this.saveState, this, {delay: 100});
67437     },
67438
67439     applyState : function(state){
67440         var cm = this.colModel,
67441             cs = state.columns,
67442             store = this.store,
67443             s,
67444             c,
67445             oldIndex;
67446
67447         if(cs){
67448             for(var i = 0, len = cs.length; i < len; i++){
67449                 s = cs[i];
67450                 c = cm.getColumnById(s.id);
67451                 if(c){
67452                     c.hidden = s.hidden;
67453                     c.width = s.width;
67454                     oldIndex = cm.getIndexById(s.id);
67455                     if(oldIndex != i){
67456                         cm.moveColumn(oldIndex, i);
67457                     }
67458                 }
67459             }
67460         }
67461         if(store){
67462             s = state.sort;
67463             if(s){
67464                 store[store.remoteSort ? 'setDefaultSort' : 'sort'](s.field, s.direction);
67465             }
67466             s = state.group;
67467             if(store.groupBy){
67468                 if(s){
67469                     store.groupBy(s);
67470                 }else{
67471                     store.clearGrouping();
67472                 }
67473             }
67474
67475         }
67476         var o = Ext.apply({}, state);
67477         delete o.columns;
67478         delete o.sort;
67479         Ext.grid.GridPanel.superclass.applyState.call(this, o);
67480     },
67481
67482     getState : function(){
67483         var o = {columns: []},
67484             store = this.store,
67485             ss,
67486             gs;
67487
67488         for(var i = 0, c; (c = this.colModel.config[i]); i++){
67489             o.columns[i] = {
67490                 id: c.id,
67491                 width: c.width
67492             };
67493             if(c.hidden){
67494                 o.columns[i].hidden = true;
67495             }
67496         }
67497         if(store){
67498             ss = store.getSortState();
67499             if(ss){
67500                 o.sort = ss;
67501             }
67502             if(store.getGroupState){
67503                 gs = store.getGroupState();
67504                 if(gs){
67505                     o.group = gs;
67506                 }
67507             }
67508         }
67509         return o;
67510     },
67511
67512     // private
67513     afterRender : function(){
67514         Ext.grid.GridPanel.superclass.afterRender.call(this);
67515         var v = this.view;
67516         this.on('bodyresize', v.layout, v);
67517         v.layout();
67518         if(this.deferRowRender){
67519             if (!this.deferRowRenderTask){
67520                 this.deferRowRenderTask = new Ext.util.DelayedTask(v.afterRender, this.view);
67521             }
67522             this.deferRowRenderTask.delay(10);
67523         }else{
67524             v.afterRender();
67525         }
67526         this.viewReady = true;
67527     },
67528
67529     /**
67530      * <p>Reconfigures the grid to use a different Store and Column Model
67531      * and fires the 'reconfigure' event. The View will be bound to the new
67532      * objects and refreshed.</p>
67533      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become
67534      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the
67535      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound
67536      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring
67537      * with the new data.</p>
67538      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object
67539      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object
67540      */
67541     reconfigure : function(store, colModel){
67542         var rendered = this.rendered;
67543         if(rendered){
67544             if(this.loadMask){
67545                 this.loadMask.destroy();
67546                 this.loadMask = new Ext.LoadMask(this.bwrap,
67547                         Ext.apply({}, {store:store}, this.initialConfig.loadMask));
67548             }
67549         }
67550         if(this.view){
67551             this.view.initData(store, colModel);
67552         }
67553         this.store = store;
67554         this.colModel = colModel;
67555         if(rendered){
67556             this.view.refresh(true);
67557         }
67558         this.fireEvent('reconfigure', this, store, colModel);
67559     },
67560
67561     // private
67562     onDestroy : function(){
67563         if (this.deferRowRenderTask && this.deferRowRenderTask.cancel){
67564             this.deferRowRenderTask.cancel();
67565         }
67566         if(this.rendered){
67567             Ext.destroy(this.view, this.loadMask);
67568         }else if(this.store && this.store.autoDestroy){
67569             this.store.destroy();
67570         }
67571         Ext.destroy(this.colModel, this.selModel);
67572         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;
67573         Ext.grid.GridPanel.superclass.onDestroy.call(this);
67574     },
67575
67576     // private
67577     processEvent : function(name, e){
67578         this.view.processEvent(name, e);
67579     },
67580
67581     // private
67582     onClick : function(e){
67583         this.processEvent('click', e);
67584     },
67585
67586     // private
67587     onMouseDown : function(e){
67588         this.processEvent('mousedown', e);
67589     },
67590
67591     // private
67592     onContextMenu : function(e, t){
67593         this.processEvent('contextmenu', e);
67594     },
67595
67596     // private
67597     onDblClick : function(e){
67598         this.processEvent('dblclick', e);
67599     },
67600
67601     // private
67602     walkCells : function(row, col, step, fn, scope){
67603         var cm    = this.colModel,
67604             clen  = cm.getColumnCount(),
67605             ds    = this.store,
67606             rlen  = ds.getCount(),
67607             first = true;
67608
67609         if(step < 0){
67610             if(col < 0){
67611                 row--;
67612                 first = false;
67613             }
67614             while(row >= 0){
67615                 if(!first){
67616                     col = clen-1;
67617                 }
67618                 first = false;
67619                 while(col >= 0){
67620                     if(fn.call(scope || this, row, col, cm) === true){
67621                         return [row, col];
67622                     }
67623                     col--;
67624                 }
67625                 row--;
67626             }
67627         } else {
67628             if(col >= clen){
67629                 row++;
67630                 first = false;
67631             }
67632             while(row < rlen){
67633                 if(!first){
67634                     col = 0;
67635                 }
67636                 first = false;
67637                 while(col < clen){
67638                     if(fn.call(scope || this, row, col, cm) === true){
67639                         return [row, col];
67640                     }
67641                     col++;
67642                 }
67643                 row++;
67644             }
67645         }
67646         return null;
67647     },
67648
67649     /**
67650      * Returns the grid's underlying element.
67651      * @return {Element} The element
67652      */
67653     getGridEl : function(){
67654         return this.body;
67655     },
67656
67657     // private for compatibility, overridden by editor grid
67658     stopEditing : Ext.emptyFn,
67659
67660     /**
67661      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>
67662      * configuration option. If no selection model was configured, this will create
67663      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.
67664      * @return {SelectionModel}
67665      */
67666     getSelectionModel : function(){
67667         if(!this.selModel){
67668             this.selModel = new Ext.grid.RowSelectionModel(
67669                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);
67670         }
67671         return this.selModel;
67672     },
67673
67674     /**
67675      * Returns the grid's data store.
67676      * @return {Ext.data.Store} The store
67677      */
67678     getStore : function(){
67679         return this.store;
67680     },
67681
67682     /**
67683      * Returns the grid's ColumnModel.
67684      * @return {Ext.grid.ColumnModel} The column model
67685      */
67686     getColumnModel : function(){
67687         return this.colModel;
67688     },
67689
67690     /**
67691      * Returns the grid's GridView object.
67692      * @return {Ext.grid.GridView} The grid view
67693      */
67694     getView : function(){
67695         if(!this.view){
67696             this.view = new Ext.grid.GridView(this.viewConfig);
67697         }
67698         return this.view;
67699     },
67700     /**
67701      * Called to get grid's drag proxy text, by default returns this.ddText.
67702      * @return {String} The text
67703      */
67704     getDragDropText : function(){
67705         var count = this.selModel.getCount();
67706         return String.format(this.ddText, count, count == 1 ? '' : 's');
67707     }
67708
67709     /**
67710      * @cfg {String/Number} activeItem
67711      * @hide
67712      */
67713     /**
67714      * @cfg {Boolean} autoDestroy
67715      * @hide
67716      */
67717     /**
67718      * @cfg {Object/String/Function} autoLoad
67719      * @hide
67720      */
67721     /**
67722      * @cfg {Boolean} autoWidth
67723      * @hide
67724      */
67725     /**
67726      * @cfg {Boolean/Number} bufferResize
67727      * @hide
67728      */
67729     /**
67730      * @cfg {String} defaultType
67731      * @hide
67732      */
67733     /**
67734      * @cfg {Object} defaults
67735      * @hide
67736      */
67737     /**
67738      * @cfg {Boolean} hideBorders
67739      * @hide
67740      */
67741     /**
67742      * @cfg {Mixed} items
67743      * @hide
67744      */
67745     /**
67746      * @cfg {String} layout
67747      * @hide
67748      */
67749     /**
67750      * @cfg {Object} layoutConfig
67751      * @hide
67752      */
67753     /**
67754      * @cfg {Boolean} monitorResize
67755      * @hide
67756      */
67757     /**
67758      * @property items
67759      * @hide
67760      */
67761     /**
67762      * @method add
67763      * @hide
67764      */
67765     /**
67766      * @method cascade
67767      * @hide
67768      */
67769     /**
67770      * @method doLayout
67771      * @hide
67772      */
67773     /**
67774      * @method find
67775      * @hide
67776      */
67777     /**
67778      * @method findBy
67779      * @hide
67780      */
67781     /**
67782      * @method findById
67783      * @hide
67784      */
67785     /**
67786      * @method findByType
67787      * @hide
67788      */
67789     /**
67790      * @method getComponent
67791      * @hide
67792      */
67793     /**
67794      * @method getLayout
67795      * @hide
67796      */
67797     /**
67798      * @method getUpdater
67799      * @hide
67800      */
67801     /**
67802      * @method insert
67803      * @hide
67804      */
67805     /**
67806      * @method load
67807      * @hide
67808      */
67809     /**
67810      * @method remove
67811      * @hide
67812      */
67813     /**
67814      * @event add
67815      * @hide
67816      */
67817     /**
67818      * @event afterlayout
67819      * @hide
67820      */
67821     /**
67822      * @event beforeadd
67823      * @hide
67824      */
67825     /**
67826      * @event beforeremove
67827      * @hide
67828      */
67829     /**
67830      * @event remove
67831      * @hide
67832      */
67833
67834
67835
67836     /**
67837      * @cfg {String} allowDomMove  @hide
67838      */
67839     /**
67840      * @cfg {String} autoEl @hide
67841      */
67842     /**
67843      * @cfg {String} applyTo  @hide
67844      */
67845     /**
67846      * @cfg {String} autoScroll  @hide
67847      */
67848     /**
67849      * @cfg {String} bodyBorder  @hide
67850      */
67851     /**
67852      * @cfg {String} bodyStyle  @hide
67853      */
67854     /**
67855      * @cfg {String} contentEl  @hide
67856      */
67857     /**
67858      * @cfg {String} disabledClass  @hide
67859      */
67860     /**
67861      * @cfg {String} elements  @hide
67862      */
67863     /**
67864      * @cfg {String} html  @hide
67865      */
67866     /**
67867      * @cfg {Boolean} preventBodyReset
67868      * @hide
67869      */
67870     /**
67871      * @property disabled
67872      * @hide
67873      */
67874     /**
67875      * @method applyToMarkup
67876      * @hide
67877      */
67878     /**
67879      * @method enable
67880      * @hide
67881      */
67882     /**
67883      * @method disable
67884      * @hide
67885      */
67886     /**
67887      * @method setDisabled
67888      * @hide
67889      */
67890 });
67891 Ext.reg('grid', Ext.grid.GridPanel);/**
67892  * @class Ext.grid.GridView
67893  * @extends Ext.util.Observable
67894  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
67895  * Methods of this class may be used to access user interface elements to enable
67896  * special display effects. Do not change the DOM structure of the user interface.</p>
67897  * <p>This class does not provide ways to manipulate the underlying data. The data
67898  * model of a Grid is held in an {@link Ext.data.Store}.</p>
67899  * @constructor
67900  * @param {Object} config
67901  */
67902 Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
67903     /**
67904      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
67905      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
67906      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
67907      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
67908      * (e.g., 'my-class another-class'). Example usage:
67909     <pre><code>
67910 viewConfig: {
67911     forceFit: true,
67912     showPreview: true, // custom property
67913     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
67914     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
67915         if(this.showPreview){
67916             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
67917             return 'x-grid3-row-expanded';
67918         }
67919         return 'x-grid3-row-collapsed';
67920     }
67921 },
67922     </code></pre>
67923      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
67924      * @param {Number} index The row index.
67925      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
67926      * customization of various aspects of a grid row.
67927      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
67928      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
67929      * <ul>
67930      * <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>
67931      * <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>
67932      * </ul>
67933      * The following property will be passed in, and may be appended to:
67934      * <ul>
67935      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
67936      * both the standard grid row, and any expansion row.</div></li>
67937      * </ul>
67938      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
67939      * @method getRowClass
67940      * @return {String} a CSS class name to add to the row.
67941      */
67942
67943     /**
67944      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
67945      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
67946      */
67947
67948     /**
67949      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
67950      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
67951     <pre><code>
67952     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
67953     </code></pre>
67954      */
67955
67956     /**
67957      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>).
67958      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
67959      * config to disable the <i>menu</i> for individual columns.  While this config is true the
67960      * following will be disabled:<div class="mdetail-params"><ul>
67961      * <li>clicking on header to sort</li>
67962      * <li>the trigger to reveal the menu.</li>
67963      * </ul></div>
67964      */
67965
67966     /**
67967      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
67968      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
67969      * See {@link Ext.grid.GridDragZone} for details.</p>
67970      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
67971      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
67972      * <li><i>after</i> the owning GridPanel has been rendered.</li>
67973      * </ul></div>
67974      * @property dragZone
67975      * @type {Ext.grid.GridDragZone}
67976      */
67977
67978     /**
67979      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
67980      * first load (defaults to <tt>true</tt>).
67981      */
67982     deferEmptyText : true,
67983
67984     /**
67985      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
67986      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
67987      * calculated.
67988      */
67989     scrollOffset : undefined,
67990
67991     /**
67992      * @cfg {Boolean} autoFill
67993      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
67994      * when the grid is <b>initially rendered</b>.  The
67995      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
67996      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
67997      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
67998      * See <tt>{@link #forceFit}</tt> also.
67999      */
68000     autoFill : false,
68001
68002     /**
68003      * @cfg {Boolean} forceFit
68004      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
68005      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
68006      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
68007      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
68008      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
68009      */
68010     forceFit : false,
68011
68012     /**
68013      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
68014      */
68015     sortClasses : ['sort-asc', 'sort-desc'],
68016
68017     /**
68018      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
68019      */
68020     sortAscText : 'Sort Ascending',
68021
68022     /**
68023      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
68024      */
68025     sortDescText : 'Sort Descending',
68026
68027     /**
68028      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
68029      */
68030     columnsText : 'Columns',
68031
68032     /**
68033      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
68034      * example overriding the default styling:
68035     <pre><code>
68036     .x-grid3-row-selected {background-color: yellow;}
68037     </code></pre>
68038      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
68039      * facets (like text) use something like:
68040     <pre><code>
68041     .x-grid3-row-selected .x-grid3-cell-inner {
68042         color: #FFCC00;
68043     }
68044     </code></pre>
68045      * @type String
68046      */
68047     selectedRowClass : 'x-grid3-row-selected',
68048
68049     // private
68050     borderWidth : 2,
68051     tdClass : 'x-grid3-cell',
68052     hdCls : 'x-grid3-hd',
68053     markDirty : true,
68054
68055     /**
68056      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
68057      */
68058     cellSelectorDepth : 4,
68059     /**
68060      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
68061      */
68062     rowSelectorDepth : 10,
68063
68064     /**
68065      * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
68066      */
68067     rowBodySelectorDepth : 10,
68068
68069     /**
68070      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
68071      */
68072     cellSelector : 'td.x-grid3-cell',
68073     /**
68074      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
68075      */
68076     rowSelector : 'div.x-grid3-row',
68077
68078     /**
68079      * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
68080      */
68081     rowBodySelector : 'div.x-grid3-row-body',
68082
68083     // private
68084     firstRowCls: 'x-grid3-row-first',
68085     lastRowCls: 'x-grid3-row-last',
68086     rowClsRe: /(?:^|\s+)x-grid3-row-(first|last|alt)(?:\s+|$)/g,
68087
68088     constructor : function(config){
68089         Ext.apply(this, config);
68090         // These events are only used internally by the grid components
68091         this.addEvents(
68092             /**
68093              * @event beforerowremoved
68094              * Internal UI Event. Fired before a row is removed.
68095              * @param {Ext.grid.GridView} view
68096              * @param {Number} rowIndex The index of the row to be removed.
68097              * @param {Ext.data.Record} record The Record to be removed
68098              */
68099             'beforerowremoved',
68100             /**
68101              * @event beforerowsinserted
68102              * Internal UI Event. Fired before rows are inserted.
68103              * @param {Ext.grid.GridView} view
68104              * @param {Number} firstRow The index of the first row to be inserted.
68105              * @param {Number} lastRow The index of the last row to be inserted.
68106              */
68107             'beforerowsinserted',
68108             /**
68109              * @event beforerefresh
68110              * Internal UI Event. Fired before the view is refreshed.
68111              * @param {Ext.grid.GridView} view
68112              */
68113             'beforerefresh',
68114             /**
68115              * @event rowremoved
68116              * Internal UI Event. Fired after a row is removed.
68117              * @param {Ext.grid.GridView} view
68118              * @param {Number} rowIndex The index of the row that was removed.
68119              * @param {Ext.data.Record} record The Record that was removed
68120              */
68121             'rowremoved',
68122             /**
68123              * @event rowsinserted
68124              * Internal UI Event. Fired after rows are inserted.
68125              * @param {Ext.grid.GridView} view
68126              * @param {Number} firstRow The index of the first inserted.
68127              * @param {Number} lastRow The index of the last row inserted.
68128              */
68129             'rowsinserted',
68130             /**
68131              * @event rowupdated
68132              * Internal UI Event. Fired after a row has been updated.
68133              * @param {Ext.grid.GridView} view
68134              * @param {Number} firstRow The index of the row updated.
68135              * @param {Ext.data.record} record The Record backing the row updated.
68136              */
68137             'rowupdated',
68138             /**
68139              * @event refresh
68140              * Internal UI Event. Fired after the GridView's body has been refreshed.
68141              * @param {Ext.grid.GridView} view
68142              */
68143             'refresh'
68144         );
68145         Ext.grid.GridView.superclass.constructor.call(this);
68146     },
68147
68148     /* -------------------------------- UI Specific ----------------------------- */
68149
68150     // private
68151     initTemplates : function(){
68152         var ts = this.templates || {};
68153         if(!ts.master){
68154             ts.master = new Ext.Template(
68155                 '<div class="x-grid3" hidefocus="true">',
68156                     '<div class="x-grid3-viewport">',
68157                         '<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>',
68158                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
68159                     '</div>',
68160                     '<div class="x-grid3-resize-marker">&#160;</div>',
68161                     '<div class="x-grid3-resize-proxy">&#160;</div>',
68162                 '</div>'
68163             );
68164         }
68165
68166         if(!ts.header){
68167             ts.header = new Ext.Template(
68168                 '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
68169                 '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
68170                 '</table>'
68171             );
68172         }
68173
68174         if(!ts.hcell){
68175             ts.hcell = new Ext.Template(
68176                 '<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>' : '',
68177                 '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
68178                 '</div></td>'
68179             );
68180         }
68181
68182         if(!ts.body){
68183             ts.body = new Ext.Template('{rows}');
68184         }
68185
68186         if(!ts.row){
68187             ts.row = new Ext.Template(
68188                 '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
68189                 '<tbody><tr>{cells}</tr>',
68190                 (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>' : ''),
68191                 '</tbody></table></div>'
68192             );
68193         }
68194
68195         if(!ts.cell){
68196             ts.cell = new Ext.Template(
68197                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
68198                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
68199                     '</td>'
68200                     );
68201         }
68202
68203         for(var k in ts){
68204             var t = ts[k];
68205             if(t && Ext.isFunction(t.compile) && !t.compiled){
68206                 t.disableFormats = true;
68207                 t.compile();
68208             }
68209         }
68210
68211         this.templates = ts;
68212         this.colRe = new RegExp('x-grid3-td-([^\\s]+)', '');
68213     },
68214
68215     // private
68216     fly : function(el){
68217         if(!this._flyweight){
68218             this._flyweight = new Ext.Element.Flyweight(document.body);
68219         }
68220         this._flyweight.dom = el;
68221         return this._flyweight;
68222     },
68223
68224     // private
68225     getEditorParent : function(){
68226         return this.scroller.dom;
68227     },
68228
68229     // private
68230     initElements : function(){
68231         var E = Ext.Element;
68232
68233         var el = this.grid.getGridEl().dom.firstChild;
68234         var cs = el.childNodes;
68235
68236         this.el = new E(el);
68237
68238         this.mainWrap = new E(cs[0]);
68239         this.mainHd = new E(this.mainWrap.dom.firstChild);
68240
68241         if(this.grid.hideHeaders){
68242             this.mainHd.setDisplayed(false);
68243         }
68244
68245         this.innerHd = this.mainHd.dom.firstChild;
68246         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
68247         if(this.forceFit){
68248             this.scroller.setStyle('overflow-x', 'hidden');
68249         }
68250         /**
68251          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
68252          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
68253          * @type Ext.Element
68254          * @property mainBody
68255          */
68256         this.mainBody = new E(this.scroller.dom.firstChild);
68257
68258         this.focusEl = new E(this.scroller.dom.childNodes[1]);
68259         this.focusEl.swallowEvent('click', true);
68260
68261         this.resizeMarker = new E(cs[1]);
68262         this.resizeProxy = new E(cs[2]);
68263     },
68264
68265     // private
68266     getRows : function(){
68267         return this.hasRows() ? this.mainBody.dom.childNodes : [];
68268     },
68269
68270     // finder methods, used with delegation
68271
68272     // private
68273     findCell : function(el){
68274         if(!el){
68275             return false;
68276         }
68277         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
68278     },
68279
68280     /**
68281      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
68282      * See also {@link #findRowIndex}
68283      * @param {HTMLElement} el The target element
68284      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
68285      */
68286     findCellIndex : function(el, requiredCls){
68287         var cell = this.findCell(el);
68288         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
68289             return this.getCellIndex(cell);
68290         }
68291         return false;
68292     },
68293
68294     // private
68295     getCellIndex : function(el){
68296         if(el){
68297             var m = el.className.match(this.colRe);
68298             if(m && m[1]){
68299                 return this.cm.getIndexById(m[1]);
68300             }
68301         }
68302         return false;
68303     },
68304
68305     // private
68306     findHeaderCell : function(el){
68307         var cell = this.findCell(el);
68308         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
68309     },
68310
68311     // private
68312     findHeaderIndex : function(el){
68313         return this.findCellIndex(el, this.hdCls);
68314     },
68315
68316     /**
68317      * Return the HtmlElement representing the grid row which contains the passed element.
68318      * @param {HTMLElement} el The target HTMLElement
68319      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
68320      */
68321     findRow : function(el){
68322         if(!el){
68323             return false;
68324         }
68325         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
68326     },
68327
68328     /**
68329      * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
68330      * See also {@link #findCellIndex}
68331      * @param {HTMLElement} el The target HTMLElement
68332      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
68333      */
68334     findRowIndex : function(el){
68335         var r = this.findRow(el);
68336         return r ? r.rowIndex : false;
68337     },
68338
68339     /**
68340      * Return the HtmlElement representing the grid row body which contains the passed element.
68341      * @param {HTMLElement} el The target HTMLElement
68342      * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
68343      */
68344     findRowBody : function(el){
68345         if(!el){
68346             return false;
68347         }
68348         return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
68349     },
68350
68351     // getter methods for fetching elements dynamically in the grid
68352
68353     /**
68354      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
68355      * @param {Number} index The row index
68356      * @return {HtmlElement} The div element.
68357      */
68358     getRow : function(row){
68359         return this.getRows()[row];
68360     },
68361
68362     /**
68363      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
68364      * @param {Number} row The row index in which to find the cell.
68365      * @param {Number} col The column index of the cell.
68366      * @return {HtmlElement} The td at the specified coordinates.
68367      */
68368     getCell : function(row, col){
68369         return this.getRow(row).getElementsByTagName('td')[col];
68370     },
68371
68372     /**
68373      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
68374      * @param {Number} index The column index
68375      * @return {HtmlElement} The td element.
68376      */
68377     getHeaderCell : function(index){
68378         return this.mainHd.dom.getElementsByTagName('td')[index];
68379     },
68380
68381     // manipulating elements
68382
68383     // private - use getRowClass to apply custom row classes
68384     addRowClass : function(row, cls){
68385         var r = this.getRow(row);
68386         if(r){
68387             this.fly(r).addClass(cls);
68388         }
68389     },
68390
68391     // private
68392     removeRowClass : function(row, cls){
68393         var r = this.getRow(row);
68394         if(r){
68395             this.fly(r).removeClass(cls);
68396         }
68397     },
68398
68399     // private
68400     removeRow : function(row){
68401         Ext.removeNode(this.getRow(row));
68402         this.syncFocusEl(row);
68403     },
68404
68405     // private
68406     removeRows : function(firstRow, lastRow){
68407         var bd = this.mainBody.dom;
68408         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
68409             Ext.removeNode(bd.childNodes[firstRow]);
68410         }
68411         this.syncFocusEl(firstRow);
68412     },
68413
68414     // scrolling stuff
68415
68416     // private
68417     getScrollState : function(){
68418         var sb = this.scroller.dom;
68419         return {left: sb.scrollLeft, top: sb.scrollTop};
68420     },
68421
68422     // private
68423     restoreScroll : function(state){
68424         var sb = this.scroller.dom;
68425         sb.scrollLeft = state.left;
68426         sb.scrollTop = state.top;
68427     },
68428
68429     /**
68430      * Scrolls the grid to the top
68431      */
68432     scrollToTop : function(){
68433         this.scroller.dom.scrollTop = 0;
68434         this.scroller.dom.scrollLeft = 0;
68435     },
68436
68437     // private
68438     syncScroll : function(){
68439         this.syncHeaderScroll();
68440         var mb = this.scroller.dom;
68441         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
68442     },
68443
68444     // private
68445     syncHeaderScroll : function(){
68446         var mb = this.scroller.dom;
68447         this.innerHd.scrollLeft = mb.scrollLeft;
68448         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
68449     },
68450
68451     // private
68452     updateSortIcon : function(col, dir){
68453         var sc = this.sortClasses;
68454         var hds = this.mainHd.select('td').removeClass(sc);
68455         hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
68456     },
68457
68458     // private
68459     updateAllColumnWidths : function(){
68460         var tw   = this.getTotalWidth(),
68461             clen = this.cm.getColumnCount(),
68462             ws   = [],
68463             len,
68464             i;
68465
68466         for(i = 0; i < clen; i++){
68467             ws[i] = this.getColumnWidth(i);
68468         }
68469
68470         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68471         this.innerHd.firstChild.firstChild.style.width = tw;
68472         this.mainBody.dom.style.width = tw;
68473
68474         for(i = 0; i < clen; i++){
68475             var hd = this.getHeaderCell(i);
68476             hd.style.width = ws[i];
68477         }
68478
68479         var ns = this.getRows(), row, trow;
68480         for(i = 0, len = ns.length; i < len; i++){
68481             row = ns[i];
68482             row.style.width = tw;
68483             if(row.firstChild){
68484                 row.firstChild.style.width = tw;
68485                 trow = row.firstChild.rows[0];
68486                 for (var j = 0; j < clen; j++) {
68487                    trow.childNodes[j].style.width = ws[j];
68488                 }
68489             }
68490         }
68491
68492         this.onAllColumnWidthsUpdated(ws, tw);
68493     },
68494
68495     // private
68496     updateColumnWidth : function(col, width){
68497         var w = this.getColumnWidth(col);
68498         var tw = this.getTotalWidth();
68499         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68500         this.innerHd.firstChild.firstChild.style.width = tw;
68501         this.mainBody.dom.style.width = tw;
68502         var hd = this.getHeaderCell(col);
68503         hd.style.width = w;
68504
68505         var ns = this.getRows(), row;
68506         for(var i = 0, len = ns.length; i < len; i++){
68507             row = ns[i];
68508             row.style.width = tw;
68509             if(row.firstChild){
68510                 row.firstChild.style.width = tw;
68511                 row.firstChild.rows[0].childNodes[col].style.width = w;
68512             }
68513         }
68514
68515         this.onColumnWidthUpdated(col, w, tw);
68516     },
68517
68518     // private
68519     updateColumnHidden : function(col, hidden){
68520         var tw = this.getTotalWidth();
68521         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68522         this.innerHd.firstChild.firstChild.style.width = tw;
68523         this.mainBody.dom.style.width = tw;
68524         var display = hidden ? 'none' : '';
68525
68526         var hd = this.getHeaderCell(col);
68527         hd.style.display = display;
68528
68529         var ns = this.getRows(), row;
68530         for(var i = 0, len = ns.length; i < len; i++){
68531             row = ns[i];
68532             row.style.width = tw;
68533             if(row.firstChild){
68534                 row.firstChild.style.width = tw;
68535                 row.firstChild.rows[0].childNodes[col].style.display = display;
68536             }
68537         }
68538
68539         this.onColumnHiddenUpdated(col, hidden, tw);
68540         delete this.lastViewWidth; // force recalc
68541         this.layout();
68542     },
68543
68544     /**
68545      * @private
68546      * Renders all of the rows to a string buffer and returns the string. This is called internally
68547      * by renderRows and performs the actual string building for the rows - it does not inject HTML into the DOM.
68548      * @param {Array} columns The column data acquired from getColumnData.
68549      * @param {Array} records The array of records to render
68550      * @param {Ext.data.Store} store The store to render the rows from
68551      * @param {Number} startRow The index of the first row being rendered. Sometimes we only render a subset of
68552      * the rows so this is used to maintain logic for striping etc
68553      * @param {Number} colCount The total number of columns in the column model
68554      * @param {Boolean} stripe True to stripe the rows
68555      * @return {String} A string containing the HTML for the rendered rows
68556      */
68557     doRender : function(columns, records, store, startRow, colCount, stripe) {
68558         var templates    = this.templates,
68559             cellTemplate = templates.cell,
68560             rowTemplate  = templates.row,
68561             last         = colCount - 1;
68562
68563         var tstyle = 'width:' + this.getTotalWidth() + ';';
68564
68565         // buffers
68566         var rowBuffer = [],
68567             colBuffer = [],
68568             rowParams = {tstyle: tstyle},
68569             meta      = {},
68570             column,
68571             record;
68572
68573         //build up each row's HTML
68574         for (var j = 0, len = records.length; j < len; j++) {
68575             record    = records[j];
68576             colBuffer = [];
68577
68578             var rowIndex = j + startRow;
68579
68580             //build up each column's HTML
68581             for (var i = 0; i < colCount; i++) {
68582                 column = columns[i];
68583
68584                 meta.id    = column.id;
68585                 meta.css   = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
68586                 meta.attr  = meta.cellAttr = '';
68587                 meta.style = column.style;
68588                 meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
68589
68590                 if (Ext.isEmpty(meta.value)) {
68591                     meta.value = '&#160;';
68592                 }
68593
68594                 if (this.markDirty && record.dirty && Ext.isDefined(record.modified[column.name])) {
68595                     meta.css += ' x-grid3-dirty-cell';
68596                 }
68597
68598                 colBuffer[colBuffer.length] = cellTemplate.apply(meta);
68599             }
68600
68601             //set up row striping and row dirtiness CSS classes
68602             var alt = [];
68603
68604             if (stripe && ((rowIndex + 1) % 2 === 0)) {
68605                 alt[0] = 'x-grid3-row-alt';
68606             }
68607
68608             if (record.dirty) {
68609                 alt[1] = ' x-grid3-dirty-row';
68610             }
68611
68612             rowParams.cols = colCount;
68613
68614             if (this.getRowClass) {
68615                 alt[2] = this.getRowClass(record, rowIndex, rowParams, store);
68616             }
68617
68618             rowParams.alt   = alt.join(' ');
68619             rowParams.cells = colBuffer.join('');
68620
68621             rowBuffer[rowBuffer.length] = rowTemplate.apply(rowParams);
68622         }
68623
68624         return rowBuffer.join('');
68625     },
68626
68627     // private
68628     processRows : function(startRow, skipStripe) {
68629         if (!this.ds || this.ds.getCount() < 1) {
68630             return;
68631         }
68632
68633         var rows = this.getRows(),
68634             len  = rows.length,
68635             i, r;
68636
68637         skipStripe = skipStripe || !this.grid.stripeRows;
68638         startRow   = startRow   || 0;
68639
68640         for (i = 0; i<len; i++) {
68641             r = rows[i];
68642             if (r) {
68643                 r.rowIndex = i;
68644                 if (!skipStripe) {
68645                     r.className = r.className.replace(this.rowClsRe, ' ');
68646                     if ((i + 1) % 2 === 0){
68647                         r.className += ' x-grid3-row-alt';
68648                     }
68649                 }
68650             }
68651         }
68652
68653         // add first/last-row classes
68654         if (startRow === 0) {
68655             Ext.fly(rows[0]).addClass(this.firstRowCls);
68656         }
68657
68658         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
68659     },
68660
68661     afterRender : function(){
68662         if(!this.ds || !this.cm){
68663             return;
68664         }
68665         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
68666         this.processRows(0, true);
68667
68668         if(this.deferEmptyText !== true){
68669             this.applyEmptyText();
68670         }
68671         this.grid.fireEvent('viewready', this.grid);
68672     },
68673
68674     /**
68675      * @private
68676      * Renders each of the UI elements in turn. This is called internally, once, by this.render. It does not
68677      * render rows from the store, just the surrounding UI elements. It also sets up listeners on the UI elements
68678      * and sets up options like column menus, moving and resizing.
68679      */
68680     renderUI : function() {
68681         var templates = this.templates,
68682             header    = this.renderHeaders(),
68683             body      = templates.body.apply({rows:'&#160;'});
68684
68685         var html = templates.master.apply({
68686             body  : body,
68687             header: header,
68688             ostyle: 'width:' + this.getOffsetWidth() + ';',
68689             bstyle: 'width:' + this.getTotalWidth()  + ';'
68690         });
68691
68692         var g = this.grid;
68693
68694         g.getGridEl().dom.innerHTML = html;
68695
68696         this.initElements();
68697
68698         // get mousedowns early
68699         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
68700
68701         this.mainHd.on({
68702             scope    : this,
68703             mouseover: this.handleHdOver,
68704             mouseout : this.handleHdOut,
68705             mousemove: this.handleHdMove
68706         });
68707
68708         this.scroller.on('scroll', this.syncScroll,  this);
68709         if (g.enableColumnResize !== false) {
68710             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
68711         }
68712
68713         if (g.enableColumnMove) {
68714             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
68715             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
68716         }
68717
68718         if (g.enableHdMenu !== false) {
68719             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
68720             this.hmenu.add(
68721                 {itemId:'asc',  text: this.sortAscText,  cls: 'xg-hmenu-sort-asc'},
68722                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
68723             );
68724
68725             if (g.enableColumnHide !== false) {
68726                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
68727                 this.colMenu.on({
68728                     scope     : this,
68729                     beforeshow: this.beforeColMenuShow,
68730                     itemclick : this.handleHdMenuClick
68731                 });
68732                 this.hmenu.add('-', {
68733                     itemId:'columns',
68734                     hideOnClick: false,
68735                     text: this.columnsText,
68736                     menu: this.colMenu,
68737                     iconCls: 'x-cols-icon'
68738                 });
68739             }
68740
68741             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
68742         }
68743
68744         if (g.trackMouseOver) {
68745             this.mainBody.on({
68746                 scope    : this,
68747                 mouseover: this.onRowOver,
68748                 mouseout : this.onRowOut
68749             });
68750         }
68751
68752         if (g.enableDragDrop || g.enableDrag) {
68753             this.dragZone = new Ext.grid.GridDragZone(g, {
68754                 ddGroup : g.ddGroup || 'GridDD'
68755             });
68756         }
68757
68758         this.updateHeaderSortState();
68759     },
68760
68761     // private
68762     processEvent : function(name, e) {
68763         var t = e.getTarget(),
68764             g = this.grid,
68765             header = this.findHeaderIndex(t);
68766         g.fireEvent(name, e);
68767         if (header !== false) {
68768             g.fireEvent('header' + name, g, header, e);
68769         } else {
68770             var row = this.findRowIndex(t),
68771                 cell,
68772                 body;
68773             if (row !== false) {
68774                 g.fireEvent('row' + name, g, row, e);
68775                 cell = this.findCellIndex(t);
68776                 if (cell !== false) {
68777                     g.fireEvent('cell' + name, g, row, cell, e);
68778                 } else {
68779                     body = this.findRowBody(t);
68780                     if (body) {
68781                         g.fireEvent('rowbody' + name, g, row, e);
68782                     }
68783                 }
68784             } else {
68785                 g.fireEvent('container' + name, g, e);
68786             }
68787         }
68788     },
68789
68790     // private
68791     layout : function() {
68792         if(!this.mainBody){
68793             return; // not rendered
68794         }
68795         var g = this.grid;
68796         var c = g.getGridEl();
68797         var csize = c.getSize(true);
68798         var vw = csize.width;
68799
68800         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
68801             return;
68802         }
68803
68804         if(g.autoHeight){
68805             this.scroller.dom.style.overflow = 'visible';
68806             if(Ext.isWebKit){
68807                 this.scroller.dom.style.position = 'static';
68808             }
68809         }else{
68810             this.el.setSize(csize.width, csize.height);
68811
68812             var hdHeight = this.mainHd.getHeight();
68813             var vh = csize.height - (hdHeight);
68814
68815             this.scroller.setSize(vw, vh);
68816             if(this.innerHd){
68817                 this.innerHd.style.width = (vw)+'px';
68818             }
68819         }
68820         if(this.forceFit){
68821             if(this.lastViewWidth != vw){
68822                 this.fitColumns(false, false);
68823                 this.lastViewWidth = vw;
68824             }
68825         }else {
68826             this.autoExpand();
68827             this.syncHeaderScroll();
68828         }
68829         this.onLayout(vw, vh);
68830     },
68831
68832     // template functions for subclasses and plugins
68833     // these functions include precalculated values
68834     onLayout : function(vw, vh){
68835         // do nothing
68836     },
68837
68838     onColumnWidthUpdated : function(col, w, tw){
68839         //template method
68840     },
68841
68842     onAllColumnWidthsUpdated : function(ws, tw){
68843         //template method
68844     },
68845
68846     onColumnHiddenUpdated : function(col, hidden, tw){
68847         // template method
68848     },
68849
68850     updateColumnText : function(col, text){
68851         // template method
68852     },
68853
68854     afterMove : function(colIndex){
68855         // template method
68856     },
68857
68858     /* ----------------------------------- Core Specific -------------------------------------------*/
68859     // private
68860     init : function(grid){
68861         this.grid = grid;
68862
68863         this.initTemplates();
68864         this.initData(grid.store, grid.colModel);
68865         this.initUI(grid);
68866     },
68867
68868     // private
68869     getColumnId : function(index){
68870       return this.cm.getColumnId(index);
68871     },
68872
68873     // private
68874     getOffsetWidth : function() {
68875         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
68876     },
68877
68878     getScrollOffset: function(){
68879         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
68880     },
68881
68882     /**
68883      * @private
68884      * Renders the header row using the 'header' template. Does not inject the HTML into the DOM, just
68885      * returns a string.
68886      * @return {String} Rendered header row
68887      */
68888     renderHeaders : function() {
68889         var cm   = this.cm,
68890             ts   = this.templates,
68891             ct   = ts.hcell,
68892             cb   = [],
68893             p    = {},
68894             len  = cm.getColumnCount(),
68895             last = len - 1;
68896
68897         for (var i = 0; i < len; i++) {
68898             p.id = cm.getColumnId(i);
68899             p.value = cm.getColumnHeader(i) || '';
68900             p.style = this.getColumnStyle(i, true);
68901             p.tooltip = this.getColumnTooltip(i);
68902             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
68903
68904             if (cm.config[i].align == 'right') {
68905                 p.istyle = 'padding-right:16px';
68906             } else {
68907                 delete p.istyle;
68908             }
68909             cb[cb.length] = ct.apply(p);
68910         }
68911         return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
68912     },
68913
68914     // private
68915     getColumnTooltip : function(i){
68916         var tt = this.cm.getColumnTooltip(i);
68917         if(tt){
68918             if(Ext.QuickTips.isEnabled()){
68919                 return 'ext:qtip="'+tt+'"';
68920             }else{
68921                 return 'title="'+tt+'"';
68922             }
68923         }
68924         return '';
68925     },
68926
68927     // private
68928     beforeUpdate : function(){
68929         this.grid.stopEditing(true);
68930     },
68931
68932     // private
68933     updateHeaders : function(){
68934         this.innerHd.firstChild.innerHTML = this.renderHeaders();
68935         this.innerHd.firstChild.style.width = this.getOffsetWidth();
68936         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
68937     },
68938
68939     /**
68940      * Focuses the specified row.
68941      * @param {Number} row The row index
68942      */
68943     focusRow : function(row){
68944         this.focusCell(row, 0, false);
68945     },
68946
68947     /**
68948      * Focuses the specified cell.
68949      * @param {Number} row The row index
68950      * @param {Number} col The column index
68951      */
68952     focusCell : function(row, col, hscroll){
68953         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
68954         if(Ext.isGecko){
68955             this.focusEl.focus();
68956         }else{
68957             this.focusEl.focus.defer(1, this.focusEl);
68958         }
68959     },
68960
68961     resolveCell : function(row, col, hscroll){
68962         if(!Ext.isNumber(row)){
68963             row = row.rowIndex;
68964         }
68965         if(!this.ds){
68966             return null;
68967         }
68968         if(row < 0 || row >= this.ds.getCount()){
68969             return null;
68970         }
68971         col = (col !== undefined ? col : 0);
68972
68973         var rowEl = this.getRow(row),
68974             cm = this.cm,
68975             colCount = cm.getColumnCount(),
68976             cellEl;
68977         if(!(hscroll === false && col === 0)){
68978             while(col < colCount && cm.isHidden(col)){
68979                 col++;
68980             }
68981             cellEl = this.getCell(row, col);
68982         }
68983
68984         return {row: rowEl, cell: cellEl};
68985     },
68986
68987     getResolvedXY : function(resolved){
68988         if(!resolved){
68989             return null;
68990         }
68991         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
68992         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
68993     },
68994
68995     syncFocusEl : function(row, col, hscroll){
68996         var xy = row;
68997         if(!Ext.isArray(xy)){
68998             row = Math.min(row, Math.max(0, this.getRows().length-1));
68999             if (isNaN(row)) {
69000                 return;
69001             }
69002             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
69003         }
69004         this.focusEl.setXY(xy||this.scroller.getXY());
69005     },
69006
69007     ensureVisible : function(row, col, hscroll){
69008         var resolved = this.resolveCell(row, col, hscroll);
69009         if(!resolved || !resolved.row){
69010             return;
69011         }
69012
69013         var rowEl = resolved.row,
69014             cellEl = resolved.cell,
69015             c = this.scroller.dom,
69016             ctop = 0,
69017             p = rowEl,
69018             stop = this.el.dom;
69019
69020         while(p && p != stop){
69021             ctop += p.offsetTop;
69022             p = p.offsetParent;
69023         }
69024
69025         ctop -= this.mainHd.dom.offsetHeight;
69026         stop = parseInt(c.scrollTop, 10);
69027
69028         var cbot = ctop + rowEl.offsetHeight,
69029             ch = c.clientHeight,
69030             sbot = stop + ch;
69031
69032
69033         if(ctop < stop){
69034           c.scrollTop = ctop;
69035         }else if(cbot > sbot){
69036             c.scrollTop = cbot-ch;
69037         }
69038
69039         if(hscroll !== false){
69040             var cleft = parseInt(cellEl.offsetLeft, 10);
69041             var cright = cleft + cellEl.offsetWidth;
69042
69043             var sleft = parseInt(c.scrollLeft, 10);
69044             var sright = sleft + c.clientWidth;
69045             if(cleft < sleft){
69046                 c.scrollLeft = cleft;
69047             }else if(cright > sright){
69048                 c.scrollLeft = cright-c.clientWidth;
69049             }
69050         }
69051         return this.getResolvedXY(resolved);
69052     },
69053
69054     // private
69055     insertRows : function(dm, firstRow, lastRow, isUpdate) {
69056         var last = dm.getCount() - 1;
69057         if( !isUpdate && firstRow === 0 && lastRow >= last) {
69058             this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
69059                 this.refresh();
69060             this.fireEvent('rowsinserted', this, firstRow, lastRow);
69061         } else {
69062             if (!isUpdate) {
69063                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
69064             }
69065             var html = this.renderRows(firstRow, lastRow),
69066                 before = this.getRow(firstRow);
69067             if (before) {
69068                 if(firstRow === 0){
69069                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
69070                 }
69071                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
69072             } else {
69073                 var r = this.getRow(last - 1);
69074                 if(r){
69075                     Ext.fly(r).removeClass(this.lastRowCls);
69076                 }
69077                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
69078             }
69079             if (!isUpdate) {
69080                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
69081                 this.processRows(firstRow);
69082             } else if (firstRow === 0 || firstRow >= last) {
69083                 //ensure first/last row is kept after an update.
69084                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
69085             }
69086         }
69087         this.syncFocusEl(firstRow);
69088     },
69089
69090     // private
69091     deleteRows : function(dm, firstRow, lastRow){
69092         if(dm.getRowCount()<1){
69093             this.refresh();
69094         }else{
69095             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
69096
69097             this.removeRows(firstRow, lastRow);
69098
69099             this.processRows(firstRow);
69100             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
69101         }
69102     },
69103
69104     // private
69105     getColumnStyle : function(col, isHeader){
69106         var style = !isHeader ? (this.cm.config[col].css || '') : '';
69107         style += 'width:'+this.getColumnWidth(col)+';';
69108         if(this.cm.isHidden(col)){
69109             style += 'display:none;';
69110         }
69111         var align = this.cm.config[col].align;
69112         if(align){
69113             style += 'text-align:'+align+';';
69114         }
69115         return style;
69116     },
69117
69118     // private
69119     getColumnWidth : function(col){
69120         var w = this.cm.getColumnWidth(col);
69121         if(Ext.isNumber(w)){
69122             return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
69123         }
69124         return w;
69125     },
69126
69127     // private
69128     getTotalWidth : function(){
69129         return this.cm.getTotalWidth()+'px';
69130     },
69131
69132     // private
69133     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
69134         var cm = this.cm, i;
69135         var tw = cm.getTotalWidth(false);
69136         var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
69137
69138         if(aw < 20){ // not initialized, so don't screw up the default widths
69139             return;
69140         }
69141         var extra = aw - tw;
69142
69143         if(extra === 0){
69144             return false;
69145         }
69146
69147         var vc = cm.getColumnCount(true);
69148         var ac = vc-(Ext.isNumber(omitColumn) ? 1 : 0);
69149         if(ac === 0){
69150             ac = 1;
69151             omitColumn = undefined;
69152         }
69153         var colCount = cm.getColumnCount();
69154         var cols = [];
69155         var extraCol = 0;
69156         var width = 0;
69157         var w;
69158         for (i = 0; i < colCount; i++){
69159             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
69160                 w = cm.getColumnWidth(i);
69161                 cols.push(i);
69162                 extraCol = i;
69163                 cols.push(w);
69164                 width += w;
69165             }
69166         }
69167         var frac = (aw - cm.getTotalWidth())/width;
69168         while (cols.length){
69169             w = cols.pop();
69170             i = cols.pop();
69171             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
69172         }
69173
69174         if((tw = cm.getTotalWidth(false)) > aw){
69175             var adjustCol = ac != vc ? omitColumn : extraCol;
69176              cm.setColumnWidth(adjustCol, Math.max(1,
69177                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
69178         }
69179
69180         if(preventRefresh !== true){
69181             this.updateAllColumnWidths();
69182         }
69183
69184
69185         return true;
69186     },
69187
69188     // private
69189     autoExpand : function(preventUpdate){
69190         var g = this.grid, cm = this.cm;
69191         if(!this.userResized && g.autoExpandColumn){
69192             var tw = cm.getTotalWidth(false);
69193             var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
69194             if(tw != aw){
69195                 var ci = cm.getIndexById(g.autoExpandColumn);
69196                 var currentWidth = cm.getColumnWidth(ci);
69197                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
69198                 if(cw != currentWidth){
69199                     cm.setColumnWidth(ci, cw, true);
69200                     if(preventUpdate !== true){
69201                         this.updateColumnWidth(ci, cw);
69202                     }
69203                 }
69204             }
69205         }
69206     },
69207
69208     /**
69209      * @private
69210      * Returns an array of column configurations - one for each column
69211      * @return {Array} Array of column config objects. This includes the column name, renderer, id style and renderer
69212      */
69213     getColumnData : function(){
69214         // build a map for all the columns
69215         var cs       = [],
69216             cm       = this.cm,
69217             colCount = cm.getColumnCount();
69218
69219         for (var i = 0; i < colCount; i++) {
69220             var name = cm.getDataIndex(i);
69221
69222             cs[i] = {
69223                 name    : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
69224                 renderer: cm.getRenderer(i),
69225                 scope   : cm.getRendererScope(i),
69226                 id      : cm.getColumnId(i),
69227                 style   : this.getColumnStyle(i)
69228             };
69229         }
69230
69231         return cs;
69232     },
69233
69234     // private
69235     renderRows : function(startRow, endRow){
69236         // pull in all the crap needed to render rows
69237         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
69238         var colCount = cm.getColumnCount();
69239
69240         if(ds.getCount() < 1){
69241             return '';
69242         }
69243
69244         var cs = this.getColumnData();
69245
69246         startRow = startRow || 0;
69247         endRow = !Ext.isDefined(endRow) ? ds.getCount()-1 : endRow;
69248
69249         // records to render
69250         var rs = ds.getRange(startRow, endRow);
69251
69252         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
69253     },
69254
69255     // private
69256     renderBody : function(){
69257         var markup = this.renderRows() || '&#160;';
69258         return this.templates.body.apply({rows: markup});
69259     },
69260
69261     // private
69262     refreshRow : function(record){
69263         var ds = this.ds, index;
69264         if(Ext.isNumber(record)){
69265             index = record;
69266             record = ds.getAt(index);
69267             if(!record){
69268                 return;
69269             }
69270         }else{
69271             index = ds.indexOf(record);
69272             if(index < 0){
69273                 return;
69274             }
69275         }
69276         this.insertRows(ds, index, index, true);
69277         this.getRow(index).rowIndex = index;
69278         this.onRemove(ds, record, index+1, true);
69279         this.fireEvent('rowupdated', this, index, record);
69280     },
69281
69282     /**
69283      * Refreshs the grid UI
69284      * @param {Boolean} headersToo (optional) True to also refresh the headers
69285      */
69286     refresh : function(headersToo){
69287         this.fireEvent('beforerefresh', this);
69288         this.grid.stopEditing(true);
69289
69290         var result = this.renderBody();
69291         this.mainBody.update(result).setWidth(this.getTotalWidth());
69292         if(headersToo === true){
69293             this.updateHeaders();
69294             this.updateHeaderSortState();
69295         }
69296         this.processRows(0, true);
69297         this.layout();
69298         this.applyEmptyText();
69299         this.fireEvent('refresh', this);
69300     },
69301
69302     /**
69303      * @private
69304      * Displays the configured emptyText if there are currently no rows to display
69305      */
69306     applyEmptyText : function(){
69307         if(this.emptyText && !this.hasRows()){
69308             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
69309         }
69310     },
69311
69312     /**
69313      * @private
69314      * Adds sorting classes to the column headers based on the bound store's sortInfo. Fires the 'sortchange' event
69315      * if the sorting has changed since this function was last run.
69316      */
69317     updateHeaderSortState : function(){
69318         var state = this.ds.getSortState();
69319         if (!state) {
69320             return;
69321         }
69322
69323         if (!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)) {
69324             this.grid.fireEvent('sortchange', this.grid, state);
69325         }
69326
69327         this.sortState = state;
69328
69329         var sortColumn = this.cm.findColumnIndex(state.field);
69330         if (sortColumn != -1){
69331             var sortDir = state.direction;
69332             this.updateSortIcon(sortColumn, sortDir);
69333         }
69334     },
69335
69336     /**
69337      * @private
69338      * Removes any sorting indicator classes from the column headers
69339      */
69340     clearHeaderSortState : function(){
69341         if (!this.sortState) {
69342             return;
69343         }
69344         this.grid.fireEvent('sortchange', this.grid, null);
69345         this.mainHd.select('td').removeClass(this.sortClasses);
69346         delete this.sortState;
69347     },
69348
69349     // private
69350     destroy : function(){
69351         if (this.scrollToTopTask && this.scrollToTopTask.cancel){
69352             this.scrollToTopTask.cancel();
69353         }
69354         if(this.colMenu){
69355             Ext.menu.MenuMgr.unregister(this.colMenu);
69356             this.colMenu.destroy();
69357             delete this.colMenu;
69358         }
69359         if(this.hmenu){
69360             Ext.menu.MenuMgr.unregister(this.hmenu);
69361             this.hmenu.destroy();
69362             delete this.hmenu;
69363         }
69364
69365         this.initData(null, null);
69366         this.purgeListeners();
69367         Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
69368
69369         if(this.grid.enableColumnMove){
69370             Ext.destroy(
69371                 this.columnDrag.el,
69372                 this.columnDrag.proxy.ghost,
69373                 this.columnDrag.proxy.el,
69374                 this.columnDrop.el,
69375                 this.columnDrop.proxyTop,
69376                 this.columnDrop.proxyBottom,
69377                 this.columnDrag.dragData.ddel,
69378                 this.columnDrag.dragData.header
69379             );
69380             if (this.columnDrag.proxy.anim) {
69381                 Ext.destroy(this.columnDrag.proxy.anim);
69382             }
69383             delete this.columnDrag.proxy.ghost;
69384             delete this.columnDrag.dragData.ddel;
69385             delete this.columnDrag.dragData.header;
69386             this.columnDrag.destroy();
69387             delete Ext.dd.DDM.locationCache[this.columnDrag.id];
69388             delete this.columnDrag._domRef;
69389
69390             delete this.columnDrop.proxyTop;
69391             delete this.columnDrop.proxyBottom;
69392             this.columnDrop.destroy();
69393             delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
69394             delete this.columnDrop._domRef;
69395             delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
69396         }
69397
69398         if (this.splitZone){ // enableColumnResize
69399             this.splitZone.destroy();
69400             delete this.splitZone._domRef;
69401             delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
69402         }
69403
69404         Ext.fly(this.innerHd).removeAllListeners();
69405         Ext.removeNode(this.innerHd);
69406         delete this.innerHd;
69407
69408         Ext.destroy(
69409             this.el,
69410             this.mainWrap,
69411             this.mainHd,
69412             this.scroller,
69413             this.mainBody,
69414             this.focusEl,
69415             this.resizeMarker,
69416             this.resizeProxy,
69417             this.activeHdBtn,
69418             this.dragZone,
69419             this.splitZone,
69420             this._flyweight
69421         );
69422
69423         delete this.grid.container;
69424
69425         if(this.dragZone){
69426             this.dragZone.destroy();
69427         }
69428
69429         Ext.dd.DDM.currentTarget = null;
69430         delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
69431
69432         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
69433     },
69434
69435     // private
69436     onDenyColumnHide : function(){
69437
69438     },
69439
69440     // private
69441     render : function(){
69442         if(this.autoFill){
69443             var ct = this.grid.ownerCt;
69444             if (ct && ct.getLayout()){
69445                 ct.on('afterlayout', function(){
69446                     this.fitColumns(true, true);
69447                     this.updateHeaders();
69448                 }, this, {single: true});
69449             }else{
69450                 this.fitColumns(true, true);
69451             }
69452         }else if(this.forceFit){
69453             this.fitColumns(true, false);
69454         }else if(this.grid.autoExpandColumn){
69455             this.autoExpand(true);
69456         }
69457
69458         this.renderUI();
69459     },
69460
69461     /* --------------------------------- Model Events and Handlers --------------------------------*/
69462     // private
69463     initData : function(ds, cm){
69464         if(this.ds){
69465             this.ds.un('load', this.onLoad, this);
69466             this.ds.un('datachanged', this.onDataChange, this);
69467             this.ds.un('add', this.onAdd, this);
69468             this.ds.un('remove', this.onRemove, this);
69469             this.ds.un('update', this.onUpdate, this);
69470             this.ds.un('clear', this.onClear, this);
69471             if(this.ds !== ds && this.ds.autoDestroy){
69472                 this.ds.destroy();
69473             }
69474         }
69475         if(ds){
69476             ds.on({
69477                 scope: this,
69478                 load: this.onLoad,
69479                 datachanged: this.onDataChange,
69480                 add: this.onAdd,
69481                 remove: this.onRemove,
69482                 update: this.onUpdate,
69483                 clear: this.onClear
69484             });
69485         }
69486         this.ds = ds;
69487
69488         if(this.cm){
69489             this.cm.un('configchange', this.onColConfigChange, this);
69490             this.cm.un('widthchange', this.onColWidthChange, this);
69491             this.cm.un('headerchange', this.onHeaderChange, this);
69492             this.cm.un('hiddenchange', this.onHiddenChange, this);
69493             this.cm.un('columnmoved', this.onColumnMove, this);
69494         }
69495         if(cm){
69496             delete this.lastViewWidth;
69497             cm.on({
69498                 scope: this,
69499                 configchange: this.onColConfigChange,
69500                 widthchange: this.onColWidthChange,
69501                 headerchange: this.onHeaderChange,
69502                 hiddenchange: this.onHiddenChange,
69503                 columnmoved: this.onColumnMove
69504             });
69505         }
69506         this.cm = cm;
69507     },
69508
69509     // private
69510     onDataChange : function(){
69511         this.refresh();
69512         this.updateHeaderSortState();
69513         this.syncFocusEl(0);
69514     },
69515
69516     // private
69517     onClear : function(){
69518         this.refresh();
69519         this.syncFocusEl(0);
69520     },
69521
69522     // private
69523     onUpdate : function(ds, record){
69524         this.refreshRow(record);
69525     },
69526
69527     // private
69528     onAdd : function(ds, records, index){
69529         this.insertRows(ds, index, index + (records.length-1));
69530     },
69531
69532     // private
69533     onRemove : function(ds, record, index, isUpdate){
69534         if(isUpdate !== true){
69535             this.fireEvent('beforerowremoved', this, index, record);
69536         }
69537         this.removeRow(index);
69538         if(isUpdate !== true){
69539             this.processRows(index);
69540             this.applyEmptyText();
69541             this.fireEvent('rowremoved', this, index, record);
69542         }
69543     },
69544
69545     // private
69546     onLoad : function(){
69547         if (Ext.isGecko){
69548             if (!this.scrollToTopTask) {
69549                 this.scrollToTopTask = new Ext.util.DelayedTask(this.scrollToTop, this);
69550             }
69551             this.scrollToTopTask.delay(1);
69552         }else{
69553             this.scrollToTop();
69554         }
69555     },
69556
69557     // private
69558     onColWidthChange : function(cm, col, width){
69559         this.updateColumnWidth(col, width);
69560     },
69561
69562     // private
69563     onHeaderChange : function(cm, col, text){
69564         this.updateHeaders();
69565     },
69566
69567     // private
69568     onHiddenChange : function(cm, col, hidden){
69569         this.updateColumnHidden(col, hidden);
69570     },
69571
69572     // private
69573     onColumnMove : function(cm, oldIndex, newIndex){
69574         this.indexMap = null;
69575         var s = this.getScrollState();
69576         this.refresh(true);
69577         this.restoreScroll(s);
69578         this.afterMove(newIndex);
69579         this.grid.fireEvent('columnmove', oldIndex, newIndex);
69580     },
69581
69582     // private
69583     onColConfigChange : function(){
69584         delete this.lastViewWidth;
69585         this.indexMap = null;
69586         this.refresh(true);
69587     },
69588
69589     /* -------------------- UI Events and Handlers ------------------------------ */
69590     // private
69591     initUI : function(grid){
69592         grid.on('headerclick', this.onHeaderClick, this);
69593     },
69594
69595     // private
69596     initEvents : function(){
69597     },
69598
69599     // private
69600     onHeaderClick : function(g, index){
69601         if(this.headersDisabled || !this.cm.isSortable(index)){
69602             return;
69603         }
69604         g.stopEditing(true);
69605         g.store.sort(this.cm.getDataIndex(index));
69606     },
69607
69608     // private
69609     onRowOver : function(e, t){
69610         var row;
69611         if((row = this.findRowIndex(t)) !== false){
69612             this.addRowClass(row, 'x-grid3-row-over');
69613         }
69614     },
69615
69616     // private
69617     onRowOut : function(e, t){
69618         var row;
69619         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
69620             this.removeRowClass(row, 'x-grid3-row-over');
69621         }
69622     },
69623
69624     // private
69625     handleWheel : function(e){
69626         e.stopPropagation();
69627     },
69628
69629     // private
69630     onRowSelect : function(row){
69631         this.addRowClass(row, this.selectedRowClass);
69632     },
69633
69634     // private
69635     onRowDeselect : function(row){
69636         this.removeRowClass(row, this.selectedRowClass);
69637     },
69638
69639     // private
69640     onCellSelect : function(row, col){
69641         var cell = this.getCell(row, col);
69642         if(cell){
69643             this.fly(cell).addClass('x-grid3-cell-selected');
69644         }
69645     },
69646
69647     // private
69648     onCellDeselect : function(row, col){
69649         var cell = this.getCell(row, col);
69650         if(cell){
69651             this.fly(cell).removeClass('x-grid3-cell-selected');
69652         }
69653     },
69654
69655     // private
69656     onColumnSplitterMoved : function(i, w){
69657         this.userResized = true;
69658         var cm = this.grid.colModel;
69659         cm.setColumnWidth(i, w, true);
69660
69661         if(this.forceFit){
69662             this.fitColumns(true, false, i);
69663             this.updateAllColumnWidths();
69664         }else{
69665             this.updateColumnWidth(i, w);
69666             this.syncHeaderScroll();
69667         }
69668
69669         this.grid.fireEvent('columnresize', i, w);
69670     },
69671
69672     // private
69673     handleHdMenuClick : function(item){
69674         var index = this.hdCtxIndex,
69675             cm = this.cm,
69676             ds = this.ds,
69677             id = item.getItemId();
69678         switch(id){
69679             case 'asc':
69680                 ds.sort(cm.getDataIndex(index), 'ASC');
69681                 break;
69682             case 'desc':
69683                 ds.sort(cm.getDataIndex(index), 'DESC');
69684                 break;
69685             default:
69686                 index = cm.getIndexById(id.substr(4));
69687                 if(index != -1){
69688                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
69689                         this.onDenyColumnHide();
69690                         return false;
69691                     }
69692                     cm.setHidden(index, item.checked);
69693                 }
69694         }
69695         return true;
69696     },
69697
69698     // private
69699     isHideableColumn : function(c){
69700         return !c.hidden;
69701     },
69702
69703     // private
69704     beforeColMenuShow : function(){
69705         var cm = this.cm,  colCount = cm.getColumnCount();
69706         this.colMenu.removeAll();
69707         for(var i = 0; i < colCount; i++){
69708             if(cm.config[i].hideable !== false){
69709                 this.colMenu.add(new Ext.menu.CheckItem({
69710                     itemId: 'col-'+cm.getColumnId(i),
69711                     text: cm.getColumnHeader(i),
69712                     checked: !cm.isHidden(i),
69713                     hideOnClick:false,
69714                     disabled: cm.config[i].hideable === false
69715                 }));
69716             }
69717         }
69718     },
69719
69720     // private
69721     handleHdDown : function(e, t){
69722         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
69723             e.stopEvent();
69724             var hd = this.findHeaderCell(t);
69725             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
69726             var index = this.getCellIndex(hd);
69727             this.hdCtxIndex = index;
69728             var ms = this.hmenu.items, cm = this.cm;
69729             ms.get('asc').setDisabled(!cm.isSortable(index));
69730             ms.get('desc').setDisabled(!cm.isSortable(index));
69731             this.hmenu.on('hide', function(){
69732                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
69733             }, this, {single:true});
69734             this.hmenu.show(t, 'tl-bl?');
69735         }
69736     },
69737
69738     // private
69739     handleHdOver : function(e, t){
69740         var hd = this.findHeaderCell(t);
69741         if(hd && !this.headersDisabled){
69742             this.activeHdRef = t;
69743             this.activeHdIndex = this.getCellIndex(hd);
69744             var fly = this.fly(hd);
69745             this.activeHdRegion = fly.getRegion();
69746             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
69747                 fly.addClass('x-grid3-hd-over');
69748                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
69749                 if(this.activeHdBtn){
69750                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
69751                 }
69752             }
69753         }
69754     },
69755
69756     // private
69757     handleHdMove : function(e, t){
69758         var hd = this.findHeaderCell(this.activeHdRef);
69759         if(hd && !this.headersDisabled){
69760             var hw = this.splitHandleWidth || 5,
69761                 r = this.activeHdRegion,
69762                 x = e.getPageX(),
69763                 ss = hd.style,
69764                 cur = '';
69765             if(this.grid.enableColumnResize !== false){
69766                 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
69767                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
69768                 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
69769                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
69770                 }
69771             }
69772             ss.cursor = cur;
69773         }
69774     },
69775
69776     // private
69777     handleHdOut : function(e, t){
69778         var hd = this.findHeaderCell(t);
69779         if(hd && (!Ext.isIE || !e.within(hd, true))){
69780             this.activeHdRef = null;
69781             this.fly(hd).removeClass('x-grid3-hd-over');
69782             hd.style.cursor = '';
69783         }
69784     },
69785
69786     // private
69787     hasRows : function(){
69788         var fc = this.mainBody.dom.firstChild;
69789         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
69790     },
69791
69792     // back compat
69793     bind : function(d, c){
69794         this.initData(d, c);
69795     }
69796 });
69797
69798
69799 // private
69800 // This is a support class used internally by the Grid components
69801 Ext.grid.GridView.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
69802     
69803     constructor: function(grid, hd){
69804         this.grid = grid;
69805         this.view = grid.getView();
69806         this.marker = this.view.resizeMarker;
69807         this.proxy = this.view.resizeProxy;
69808         Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
69809             'gridSplitters' + this.grid.getGridEl().id, {
69810             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
69811         });
69812         this.scroll = false;
69813         this.hw = this.view.splitHandleWidth || 5;
69814     },
69815
69816     b4StartDrag : function(x, y){
69817         this.dragHeadersDisabled = this.view.headersDisabled;
69818         this.view.headersDisabled = true;
69819         var h = this.view.mainWrap.getHeight();
69820         this.marker.setHeight(h);
69821         this.marker.show();
69822         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
69823         this.proxy.setHeight(h);
69824         var w = this.cm.getColumnWidth(this.cellIndex),
69825             minw = Math.max(w-this.grid.minColumnWidth, 0);
69826         this.resetConstraints();
69827         this.setXConstraint(minw, 1000);
69828         this.setYConstraint(0, 0);
69829         this.minX = x - minw;
69830         this.maxX = x + 1000;
69831         this.startPos = x;
69832         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
69833     },
69834
69835     allowHeaderDrag : function(e){
69836         return true;
69837     },
69838
69839     handleMouseDown : function(e){
69840         var t = this.view.findHeaderCell(e.getTarget());
69841         if(t && this.allowHeaderDrag(e)){
69842             var xy = this.view.fly(t).getXY(), 
69843                 x = xy[0], 
69844                 y = xy[1],
69845                 exy = e.getXY(), ex = exy[0],
69846                 w = t.offsetWidth, adjust = false;
69847                 
69848             if((ex - x) <= this.hw){
69849                 adjust = -1;
69850             }else if((x+w) - ex <= this.hw){
69851                 adjust = 0;
69852             }
69853             if(adjust !== false){
69854                 this.cm = this.grid.colModel;
69855                 var ci = this.view.getCellIndex(t);
69856                 if(adjust == -1){
69857                   if (ci + adjust < 0) {
69858                     return;
69859                   }
69860                     while(this.cm.isHidden(ci+adjust)){
69861                         --adjust;
69862                         if(ci+adjust < 0){
69863                             return;
69864                         }
69865                     }
69866                 }
69867                 this.cellIndex = ci+adjust;
69868                 this.split = t.dom;
69869                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
69870                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
69871                 }
69872             }else if(this.view.columnDrag){
69873                 this.view.columnDrag.callHandleMouseDown(e);
69874             }
69875         }
69876     },
69877
69878     endDrag : function(e){
69879         this.marker.hide();
69880         var v = this.view,
69881             endX = Math.max(this.minX, e.getPageX()),
69882             diff = endX - this.startPos,
69883             disabled = this.dragHeadersDisabled;
69884             
69885         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
69886         setTimeout(function(){
69887             v.headersDisabled = disabled;
69888         }, 50);
69889     },
69890
69891     autoOffset : function(){
69892         this.setDelta(0,0);
69893     }
69894 });
69895 // private
69896 // This is a support class used internally by the Grid components
69897 Ext.grid.HeaderDragZone = Ext.extend(Ext.dd.DragZone, {
69898     maxDragWidth: 120,
69899     
69900     constructor : function(grid, hd, hd2){
69901         this.grid = grid;
69902         this.view = grid.getView();
69903         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
69904         Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);
69905         if(hd2){
69906             this.setHandleElId(Ext.id(hd));
69907             this.setOuterHandleElId(Ext.id(hd2));
69908         }
69909         this.scroll = false;
69910     },
69911     
69912     getDragData : function(e){
69913         var t = Ext.lib.Event.getTarget(e),
69914             h = this.view.findHeaderCell(t);
69915         if(h){
69916             return {ddel: h.firstChild, header:h};
69917         }
69918         return false;
69919     },
69920
69921     onInitDrag : function(e){
69922         // keep the value here so we can restore it;
69923         this.dragHeadersDisabled = this.view.headersDisabled;
69924         this.view.headersDisabled = true;
69925         var clone = this.dragData.ddel.cloneNode(true);
69926         clone.id = Ext.id();
69927         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
69928         this.proxy.update(clone);
69929         return true;
69930     },
69931
69932     afterValidDrop : function(){
69933         this.completeDrop();
69934     },
69935
69936     afterInvalidDrop : function(){
69937         this.completeDrop();
69938     },
69939     
69940     completeDrop: function(){
69941         var v = this.view,
69942             disabled = this.dragHeadersDisabled;
69943         setTimeout(function(){
69944             v.headersDisabled = disabled;
69945         }, 50);
69946     }
69947 });
69948
69949 // private
69950 // This is a support class used internally by the Grid components
69951 Ext.grid.HeaderDropZone = Ext.extend(Ext.dd.DropZone, {
69952     proxyOffsets : [-4, -9],
69953     fly: Ext.Element.fly,
69954     
69955     constructor : function(grid, hd, hd2){
69956         this.grid = grid;
69957         this.view = grid.getView();
69958         // split the proxies so they don't interfere with mouse events
69959         this.proxyTop = Ext.DomHelper.append(document.body, {
69960             cls:"col-move-top", html:"&#160;"
69961         }, true);
69962         this.proxyBottom = Ext.DomHelper.append(document.body, {
69963             cls:"col-move-bottom", html:"&#160;"
69964         }, true);
69965         this.proxyTop.hide = this.proxyBottom.hide = function(){
69966             this.setLeftTop(-100,-100);
69967             this.setStyle("visibility", "hidden");
69968         };
69969         this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
69970         // temporarily disabled
69971         //Ext.dd.ScrollManager.register(this.view.scroller.dom);
69972         Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
69973     },
69974
69975     getTargetFromEvent : function(e){
69976         var t = Ext.lib.Event.getTarget(e),
69977             cindex = this.view.findCellIndex(t);
69978         if(cindex !== false){
69979             return this.view.getHeaderCell(cindex);
69980         }
69981     },
69982
69983     nextVisible : function(h){
69984         var v = this.view, cm = this.grid.colModel;
69985         h = h.nextSibling;
69986         while(h){
69987             if(!cm.isHidden(v.getCellIndex(h))){
69988                 return h;
69989             }
69990             h = h.nextSibling;
69991         }
69992         return null;
69993     },
69994
69995     prevVisible : function(h){
69996         var v = this.view, cm = this.grid.colModel;
69997         h = h.prevSibling;
69998         while(h){
69999             if(!cm.isHidden(v.getCellIndex(h))){
70000                 return h;
70001             }
70002             h = h.prevSibling;
70003         }
70004         return null;
70005     },
70006
70007     positionIndicator : function(h, n, e){
70008         var x = Ext.lib.Event.getPageX(e),
70009             r = Ext.lib.Dom.getRegion(n.firstChild),
70010             px, 
70011             pt, 
70012             py = r.top + this.proxyOffsets[1];
70013         if((r.right - x) <= (r.right-r.left)/2){
70014             px = r.right+this.view.borderWidth;
70015             pt = "after";
70016         }else{
70017             px = r.left;
70018             pt = "before";
70019         }
70020
70021         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){
70022             return false;
70023         }
70024
70025         px +=  this.proxyOffsets[0];
70026         this.proxyTop.setLeftTop(px, py);
70027         this.proxyTop.show();
70028         if(!this.bottomOffset){
70029             this.bottomOffset = this.view.mainHd.getHeight();
70030         }
70031         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
70032         this.proxyBottom.show();
70033         return pt;
70034     },
70035
70036     onNodeEnter : function(n, dd, e, data){
70037         if(data.header != n){
70038             this.positionIndicator(data.header, n, e);
70039         }
70040     },
70041
70042     onNodeOver : function(n, dd, e, data){
70043         var result = false;
70044         if(data.header != n){
70045             result = this.positionIndicator(data.header, n, e);
70046         }
70047         if(!result){
70048             this.proxyTop.hide();
70049             this.proxyBottom.hide();
70050         }
70051         return result ? this.dropAllowed : this.dropNotAllowed;
70052     },
70053
70054     onNodeOut : function(n, dd, e, data){
70055         this.proxyTop.hide();
70056         this.proxyBottom.hide();
70057     },
70058
70059     onNodeDrop : function(n, dd, e, data){
70060         var h = data.header;
70061         if(h != n){
70062             var cm = this.grid.colModel,
70063                 x = Ext.lib.Event.getPageX(e),
70064                 r = Ext.lib.Dom.getRegion(n.firstChild),
70065                 pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before",
70066                 oldIndex = this.view.getCellIndex(h),
70067                 newIndex = this.view.getCellIndex(n);
70068             if(pt == "after"){
70069                 newIndex++;
70070             }
70071             if(oldIndex < newIndex){
70072                 newIndex--;
70073             }
70074             cm.moveColumn(oldIndex, newIndex);
70075             return true;
70076         }
70077         return false;
70078     }
70079 });
70080
70081 Ext.grid.GridView.ColumnDragZone = Ext.extend(Ext.grid.HeaderDragZone, {
70082     
70083     constructor : function(grid, hd){
70084         Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
70085         this.proxy.el.addClass('x-grid3-col-dd');
70086     },
70087     
70088     handleMouseDown : function(e){
70089     },
70090
70091     callHandleMouseDown : function(e){
70092         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
70093     }
70094 });// private
70095 // This is a support class used internally by the Grid components
70096 Ext.grid.SplitDragZone = Ext.extend(Ext.dd.DDProxy, {
70097     fly: Ext.Element.fly,
70098     
70099     constructor : function(grid, hd, hd2){
70100         this.grid = grid;
70101         this.view = grid.getView();
70102         this.proxy = this.view.resizeProxy;
70103         Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,
70104             "gridSplitters" + this.grid.getGridEl().id, {
70105             dragElId : Ext.id(this.proxy.dom), resizeFrame:false
70106         });
70107         this.setHandleElId(Ext.id(hd));
70108         this.setOuterHandleElId(Ext.id(hd2));
70109         this.scroll = false;
70110     },
70111
70112     b4StartDrag : function(x, y){
70113         this.view.headersDisabled = true;
70114         this.proxy.setHeight(this.view.mainWrap.getHeight());
70115         var w = this.cm.getColumnWidth(this.cellIndex);
70116         var minw = Math.max(w-this.grid.minColumnWidth, 0);
70117         this.resetConstraints();
70118         this.setXConstraint(minw, 1000);
70119         this.setYConstraint(0, 0);
70120         this.minX = x - minw;
70121         this.maxX = x + 1000;
70122         this.startPos = x;
70123         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
70124     },
70125
70126
70127     handleMouseDown : function(e){
70128         var ev = Ext.EventObject.setEvent(e);
70129         var t = this.fly(ev.getTarget());
70130         if(t.hasClass("x-grid-split")){
70131             this.cellIndex = this.view.getCellIndex(t.dom);
70132             this.split = t.dom;
70133             this.cm = this.grid.colModel;
70134             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
70135                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
70136             }
70137         }
70138     },
70139
70140     endDrag : function(e){
70141         this.view.headersDisabled = false;
70142         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));
70143         var diff = endX - this.startPos;
70144         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
70145     },
70146
70147     autoOffset : function(){
70148         this.setDelta(0,0);
70149     }
70150 });/**
70151  * @class Ext.grid.GridDragZone
70152  * @extends Ext.dd.DragZone
70153  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the
70154  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>
70155  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of
70156  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
70157  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able
70158  * to process the {@link #getDragData data} which is provided.
70159  */
70160 Ext.grid.GridDragZone = function(grid, config){
70161     this.view = grid.getView();
70162     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
70163     this.scroll = false;
70164     this.grid = grid;
70165     this.ddel = document.createElement('div');
70166     this.ddel.className = 'x-grid-dd-wrap';
70167 };
70168
70169 Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {
70170     ddGroup : "GridDD",
70171
70172     /**
70173      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>
70174      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},
70175      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>
70176      * <p>The data object contains the following properties:<ul>
70177      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>
70178      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>
70179      * <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>
70180      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>
70181      * </ul></p>
70182      */
70183     getDragData : function(e){
70184         var t = Ext.lib.Event.getTarget(e);
70185         var rowIndex = this.view.findRowIndex(t);
70186         if(rowIndex !== false){
70187             var sm = this.grid.selModel;
70188             if(!sm.isSelected(rowIndex) || e.hasModifier()){
70189                 sm.handleMouseDown(this.grid, rowIndex, e);
70190             }
70191             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
70192         }
70193         return false;
70194     },
70195
70196     /**
70197      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"
70198      * of the data being dragged.</p>
70199      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>
70200      */
70201     onInitDrag : function(e){
70202         var data = this.dragData;
70203         this.ddel.innerHTML = this.grid.getDragDropText();
70204         this.proxy.update(this.ddel);
70205         // fire start drag?
70206     },
70207
70208     /**
70209      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight
70210      * the selected rows to show that they have not been dragged.
70211      */
70212     afterRepair : function(){
70213         this.dragging = false;
70214     },
70215
70216     /**
70217      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>
70218      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>
70219      * @param {EventObject} e The mouse up event
70220      * @return {Array} The xy location (e.g. [100, 200])
70221      */
70222     getRepairXY : function(e, data){
70223         return false;
70224     },
70225
70226     onEndDrag : function(data, e){
70227         // fire end drag?
70228     },
70229
70230     onValidDrop : function(dd, e, id){
70231         // fire drag drop?
70232         this.hideProxy();
70233     },
70234
70235     beforeInvalidDrop : function(e, id){
70236
70237     }
70238 });
70239 /**
70240  * @class Ext.grid.ColumnModel
70241  * @extends Ext.util.Observable
70242  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),
70243  * the ColumnModel is used to configure how and what parts of that data will be displayed in the
70244  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
70245  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>
70246  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the
70247  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>
70248  * <pre><code>
70249 {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}
70250  * </code></pre>
70251  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
70252  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within
70253  * each record in the store is indexed into the ColumnModel.</p>
70254  * <p>There are two ways to initialize the ColumnModel class:</p>
70255  * <p><u>Initialization Method 1: an Array</u></p>
70256 <pre><code>
70257  var colModel = new Ext.grid.ColumnModel([
70258     { header: "Ticker", width: 60, sortable: true},
70259     { header: "Company Name", width: 150, sortable: true, id: 'company'},
70260     { header: "Market Cap.", width: 100, sortable: true},
70261     { header: "$ Sales", width: 100, sortable: true, renderer: money},
70262     { header: "Employees", width: 100, sortable: true, resizable: false}
70263  ]);
70264  </code></pre>
70265  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
70266  * objects to define the initial layout / display of the columns in the Grid. The order of each
70267  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
70268  * order of the column display.  A Column's display may be initially hidden using the
70269  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column
70270  * header menu).  Fields that are not included in the ColumnModel will not be displayable at all.</p>
70271  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
70272  * {@link Ext.data.Store Store} the column draws its data from is configured through the
70273  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the
70274  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the
70275  * example above) it will use the column configuration's index in the Array as the index.</p>
70276  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>
70277  * <p><u>Initialization Method 2: an Object</u></p>
70278  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to
70279  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>
70280  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults
70281  * for all columns, e.g.:</p><pre><code>
70282  var colModel = new Ext.grid.ColumnModel({
70283     columns: [
70284         { header: "Ticker", width: 60, menuDisabled: false},
70285         { header: "Company Name", width: 150, id: 'company'},
70286         { header: "Market Cap."},
70287         { header: "$ Sales", renderer: money},
70288         { header: "Employees", resizable: false}
70289     ],
70290     defaults: {
70291         sortable: true,
70292         menuDisabled: true,
70293         width: 100
70294     },
70295     listeners: {
70296         {@link #hiddenchange}: function(cm, colIndex, hidden) {
70297             saveConfig(colIndex, hidden);
70298         }
70299     }
70300 });
70301  </code></pre>
70302  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the
70303  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config
70304  * option. This column could be styled by including the following css:</p><pre><code>
70305  //add this css *after* the core css is loaded
70306 .x-grid3-td-company {
70307     color: red; // entire column will have red font
70308 }
70309 // modify the header row only, adding an icon to the column header
70310 .x-grid3-hd-company {
70311     background: transparent
70312         url(../../resources/images/icons/silk/building.png)
70313         no-repeat 3px 3px ! important;
70314         padding-left:20px;
70315 }
70316  </code></pre>
70317  * Note that the "Company Name" column could be specified as the
70318  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.
70319  * @constructor
70320  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
70321  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
70322  */
70323 Ext.grid.ColumnModel = Ext.extend(Ext.util.Observable, {
70324     /**
70325      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>
70326      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the
70327      * <tt><b>{@link #defaults}</b></tt> config property.
70328      */
70329     defaultWidth: 100,
70330     /**
70331      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
70332      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured
70333      * through the <tt><b>{@link #defaults}</b></tt> config property.
70334      */
70335     defaultSortable: false,
70336     /**
70337      * @cfg {Array} columns An Array of object literals.  The config options defined by
70338      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each
70339      * individual column definition.
70340      */
70341     /**
70342      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
70343      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with
70344      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.
70345      */
70346
70347     constructor : function(config){
70348         /**
70349              * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
70350              * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
70351              * be specified.
70352              * @property config
70353              * @type Array
70354              */
70355             if(config.columns){
70356                 Ext.apply(this, config);
70357                 this.setConfig(config.columns, true);
70358             }else{
70359                 this.setConfig(config, true);
70360             }
70361             this.addEvents(
70362                 /**
70363                  * @event widthchange
70364                  * Fires when the width of a column is programmaticially changed using
70365                  * <code>{@link #setColumnWidth}</code>.
70366                  * Note internal resizing suppresses the event from firing. See also
70367                  * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.
70368                  * @param {ColumnModel} this
70369                  * @param {Number} columnIndex The column index
70370                  * @param {Number} newWidth The new width
70371                  */
70372                 "widthchange",
70373                 /**
70374                  * @event headerchange
70375                  * Fires when the text of a header changes.
70376                  * @param {ColumnModel} this
70377                  * @param {Number} columnIndex The column index
70378                  * @param {String} newText The new header text
70379                  */
70380                 "headerchange",
70381                 /**
70382                  * @event hiddenchange
70383                  * Fires when a column is hidden or "unhidden".
70384                  * @param {ColumnModel} this
70385                  * @param {Number} columnIndex The column index
70386                  * @param {Boolean} hidden true if hidden, false otherwise
70387                  */
70388                 "hiddenchange",
70389                 /**
70390                  * @event columnmoved
70391                  * Fires when a column is moved.
70392                  * @param {ColumnModel} this
70393                  * @param {Number} oldIndex
70394                  * @param {Number} newIndex
70395                  */
70396                 "columnmoved",
70397                 /**
70398                  * @event configchange
70399                  * Fires when the configuration is changed
70400                  * @param {ColumnModel} this
70401                  */
70402                 "configchange"
70403             );
70404             Ext.grid.ColumnModel.superclass.constructor.call(this);
70405     },
70406
70407     /**
70408      * Returns the id of the column at the specified index.
70409      * @param {Number} index The column index
70410      * @return {String} the id
70411      */
70412     getColumnId : function(index){
70413         return this.config[index].id;
70414     },
70415
70416     getColumnAt : function(index){
70417         return this.config[index];
70418     },
70419
70420     /**
70421      * <p>Reconfigures this column model according to the passed Array of column definition objects.
70422      * For a description of the individual properties of a column definition object, see the
70423      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>
70424      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
70425      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>
70426      * @param {Array} config Array of Column definition objects.
70427      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>
70428      * and destroys existing editors.
70429      */
70430     setConfig : function(config, initial){
70431         var i, c, len;
70432         if(!initial){ // cleanup
70433             delete this.totalWidth;
70434             for(i = 0, len = this.config.length; i < len; i++){
70435                 c = this.config[i];
70436                 if(c.setEditor){
70437                     //check here, in case we have a special column like a CheckboxSelectionModel
70438                     c.setEditor(null);
70439                 }
70440             }
70441         }
70442
70443         // backward compatibility
70444         this.defaults = Ext.apply({
70445             width: this.defaultWidth,
70446             sortable: this.defaultSortable
70447         }, this.defaults);
70448
70449         this.config = config;
70450         this.lookup = {};
70451
70452         for(i = 0, len = config.length; i < len; i++){
70453             c = Ext.applyIf(config[i], this.defaults);
70454             // if no id, create one using column's ordinal position
70455             if(Ext.isEmpty(c.id)){
70456                 c.id = i;
70457             }
70458             if(!c.isColumn){
70459                 var Cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
70460                 c = new Cls(c);
70461                 config[i] = c;
70462             }
70463             this.lookup[c.id] = c;
70464         }
70465         if(!initial){
70466             this.fireEvent('configchange', this);
70467         }
70468     },
70469
70470     /**
70471      * Returns the column for a specified id.
70472      * @param {String} id The column id
70473      * @return {Object} the column
70474      */
70475     getColumnById : function(id){
70476         return this.lookup[id];
70477     },
70478
70479     /**
70480      * Returns the index for a specified column id.
70481      * @param {String} id The column id
70482      * @return {Number} the index, or -1 if not found
70483      */
70484     getIndexById : function(id){
70485         for(var i = 0, len = this.config.length; i < len; i++){
70486             if(this.config[i].id == id){
70487                 return i;
70488             }
70489         }
70490         return -1;
70491     },
70492
70493     /**
70494      * Moves a column from one position to another.
70495      * @param {Number} oldIndex The index of the column to move.
70496      * @param {Number} newIndex The position at which to reinsert the coolumn.
70497      */
70498     moveColumn : function(oldIndex, newIndex){
70499         var c = this.config[oldIndex];
70500         this.config.splice(oldIndex, 1);
70501         this.config.splice(newIndex, 0, c);
70502         this.dataMap = null;
70503         this.fireEvent("columnmoved", this, oldIndex, newIndex);
70504     },
70505
70506     /**
70507      * Returns the number of columns.
70508      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
70509      * @return {Number}
70510      */
70511     getColumnCount : function(visibleOnly){
70512         if(visibleOnly === true){
70513             var c = 0;
70514             for(var i = 0, len = this.config.length; i < len; i++){
70515                 if(!this.isHidden(i)){
70516                     c++;
70517                 }
70518             }
70519             return c;
70520         }
70521         return this.config.length;
70522     },
70523
70524     /**
70525      * Returns the column configs that return true by the passed function that is called
70526      * with (columnConfig, index)
70527 <pre><code>
70528 // returns an array of column config objects for all hidden columns
70529 var columns = grid.getColumnModel().getColumnsBy(function(c){
70530   return c.hidden;
70531 });
70532 </code></pre>
70533      * @param {Function} fn A function which, when passed a {@link Ext.grid.Column Column} object, must
70534      * return <code>true</code> if the column is to be included in the returned Array.
70535      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
70536      * is executed. Defaults to this ColumnModel.
70537      * @return {Array} result
70538      */
70539     getColumnsBy : function(fn, scope){
70540         var r = [];
70541         for(var i = 0, len = this.config.length; i < len; i++){
70542             var c = this.config[i];
70543             if(fn.call(scope||this, c, i) === true){
70544                 r[r.length] = c;
70545             }
70546         }
70547         return r;
70548     },
70549
70550     /**
70551      * Returns true if the specified column is sortable.
70552      * @param {Number} col The column index
70553      * @return {Boolean}
70554      */
70555     isSortable : function(col){
70556         return !!this.config[col].sortable;
70557     },
70558
70559     /**
70560      * Returns true if the specified column menu is disabled.
70561      * @param {Number} col The column index
70562      * @return {Boolean}
70563      */
70564     isMenuDisabled : function(col){
70565         return !!this.config[col].menuDisabled;
70566     },
70567
70568     /**
70569      * Returns the rendering (formatting) function defined for the column.
70570      * @param {Number} col The column index.
70571      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
70572      */
70573     getRenderer : function(col){
70574         if(!this.config[col].renderer){
70575             return Ext.grid.ColumnModel.defaultRenderer;
70576         }
70577         return this.config[col].renderer;
70578     },
70579
70580     getRendererScope : function(col){
70581         return this.config[col].scope;
70582     },
70583
70584     /**
70585      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
70586      * default formatting functions.
70587      * @param {Number} col The column index
70588      * @param {Function} fn The function to use to process the cell's raw data
70589      * to return HTML markup for the grid view. The render function is called with
70590      * the following parameters:<ul>
70591      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
70592      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
70593      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
70594      * <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
70595      * (e.g. 'style="color:red;"').</p></li></ul></p></li>
70596      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>
70597      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
70598      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
70599      * <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>
70600      */
70601     setRenderer : function(col, fn){
70602         this.config[col].renderer = fn;
70603     },
70604
70605     /**
70606      * Returns the width for the specified column.
70607      * @param {Number} col The column index
70608      * @return {Number}
70609      */
70610     getColumnWidth : function(col){
70611         return this.config[col].width;
70612     },
70613
70614     /**
70615      * Sets the width for a column.
70616      * @param {Number} col The column index
70617      * @param {Number} width The new width
70618      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>
70619      * event. Defaults to false.
70620      */
70621     setColumnWidth : function(col, width, suppressEvent){
70622         this.config[col].width = width;
70623         this.totalWidth = null;
70624         if(!suppressEvent){
70625              this.fireEvent("widthchange", this, col, width);
70626         }
70627     },
70628
70629     /**
70630      * Returns the total width of all columns.
70631      * @param {Boolean} includeHidden True to include hidden column widths
70632      * @return {Number}
70633      */
70634     getTotalWidth : function(includeHidden){
70635         if(!this.totalWidth){
70636             this.totalWidth = 0;
70637             for(var i = 0, len = this.config.length; i < len; i++){
70638                 if(includeHidden || !this.isHidden(i)){
70639                     this.totalWidth += this.getColumnWidth(i);
70640                 }
70641             }
70642         }
70643         return this.totalWidth;
70644     },
70645
70646     /**
70647      * Returns the header for the specified column.
70648      * @param {Number} col The column index
70649      * @return {String}
70650      */
70651     getColumnHeader : function(col){
70652         return this.config[col].header;
70653     },
70654
70655     /**
70656      * Sets the header for a column.
70657      * @param {Number} col The column index
70658      * @param {String} header The new header
70659      */
70660     setColumnHeader : function(col, header){
70661         this.config[col].header = header;
70662         this.fireEvent("headerchange", this, col, header);
70663     },
70664
70665     /**
70666      * Returns the tooltip for the specified column.
70667      * @param {Number} col The column index
70668      * @return {String}
70669      */
70670     getColumnTooltip : function(col){
70671             return this.config[col].tooltip;
70672     },
70673     /**
70674      * Sets the tooltip for a column.
70675      * @param {Number} col The column index
70676      * @param {String} tooltip The new tooltip
70677      */
70678     setColumnTooltip : function(col, tooltip){
70679             this.config[col].tooltip = tooltip;
70680     },
70681
70682     /**
70683      * Returns the dataIndex for the specified column.
70684 <pre><code>
70685 // Get field name for the column
70686 var fieldName = grid.getColumnModel().getDataIndex(columnIndex);
70687 </code></pre>
70688      * @param {Number} col The column index
70689      * @return {String} The column's dataIndex
70690      */
70691     getDataIndex : function(col){
70692         return this.config[col].dataIndex;
70693     },
70694
70695     /**
70696      * Sets the dataIndex for a column.
70697      * @param {Number} col The column index
70698      * @param {String} dataIndex The new dataIndex
70699      */
70700     setDataIndex : function(col, dataIndex){
70701         this.config[col].dataIndex = dataIndex;
70702     },
70703
70704     /**
70705      * Finds the index of the first matching column for the given dataIndex.
70706      * @param {String} col The dataIndex to find
70707      * @return {Number} The column index, or -1 if no match was found
70708      */
70709     findColumnIndex : function(dataIndex){
70710         var c = this.config;
70711         for(var i = 0, len = c.length; i < len; i++){
70712             if(c[i].dataIndex == dataIndex){
70713                 return i;
70714             }
70715         }
70716         return -1;
70717     },
70718
70719     /**
70720      * Returns true if the cell is editable.
70721 <pre><code>
70722 var store = new Ext.data.Store({...});
70723 var colModel = new Ext.grid.ColumnModel({
70724   columns: [...],
70725   isCellEditable: function(col, row) {
70726     var record = store.getAt(row);
70727     if (record.get('readonly')) { // replace with your condition
70728       return false;
70729     }
70730     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
70731   }
70732 });
70733 var grid = new Ext.grid.GridPanel({
70734   store: store,
70735   colModel: colModel,
70736   ...
70737 });
70738 </code></pre>
70739      * @param {Number} colIndex The column index
70740      * @param {Number} rowIndex The row index
70741      * @return {Boolean}
70742      */
70743     isCellEditable : function(colIndex, rowIndex){
70744         var c = this.config[colIndex],
70745             ed = c.editable;
70746
70747         //force boolean
70748         return !!(ed || (!Ext.isDefined(ed) && c.editor));
70749     },
70750
70751     /**
70752      * Returns the editor defined for the cell/column.
70753      * @param {Number} colIndex The column index
70754      * @param {Number} rowIndex The row index
70755      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
70756      * the {@link Ext.form.Field Field} used to edit the cell.
70757      */
70758     getCellEditor : function(colIndex, rowIndex){
70759         return this.config[colIndex].getCellEditor(rowIndex);
70760     },
70761
70762     /**
70763      * Sets if a column is editable.
70764      * @param {Number} col The column index
70765      * @param {Boolean} editable True if the column is editable
70766      */
70767     setEditable : function(col, editable){
70768         this.config[col].editable = editable;
70769     },
70770
70771     /**
70772      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#hidden hidden}</code>,
70773      * <tt>false</tt> otherwise.
70774      * @param {Number} colIndex The column index
70775      * @return {Boolean}
70776      */
70777     isHidden : function(colIndex){
70778         return !!this.config[colIndex].hidden; // ensure returns boolean
70779     },
70780
70781     /**
70782      * Returns <tt>true</tt> if the column is <code>{@link Ext.grid.Column#fixed fixed}</code>,
70783      * <tt>false</tt> otherwise.
70784      * @param {Number} colIndex The column index
70785      * @return {Boolean}
70786      */
70787     isFixed : function(colIndex){
70788         return !!this.config[colIndex].fixed;
70789     },
70790
70791     /**
70792      * Returns true if the column can be resized
70793      * @return {Boolean}
70794      */
70795     isResizable : function(colIndex){
70796         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
70797     },
70798     /**
70799      * Sets if a column is hidden.
70800 <pre><code>
70801 myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).
70802 </code></pre>
70803      * @param {Number} colIndex The column index
70804      * @param {Boolean} hidden True if the column is hidden
70805      */
70806     setHidden : function(colIndex, hidden){
70807         var c = this.config[colIndex];
70808         if(c.hidden !== hidden){
70809             c.hidden = hidden;
70810             this.totalWidth = null;
70811             this.fireEvent("hiddenchange", this, colIndex, hidden);
70812         }
70813     },
70814
70815     /**
70816      * Sets the editor for a column and destroys the prior editor.
70817      * @param {Number} col The column index
70818      * @param {Object} editor The editor object
70819      */
70820     setEditor : function(col, editor){
70821         this.config[col].setEditor(editor);
70822     },
70823
70824     /**
70825      * Destroys this column model by purging any event listeners, and removing any editors.
70826      */
70827     destroy : function(){
70828         var c;
70829         for(var i = 0, len = this.config.length; i < len; i++){
70830             c = this.config[i];
70831             if(c.setEditor){
70832                 c.setEditor(null);
70833             }
70834         }
70835         this.purgeListeners();
70836     }
70837 });
70838
70839 // private
70840 Ext.grid.ColumnModel.defaultRenderer = function(value){
70841     if(typeof value == "string" && value.length < 1){
70842         return "&#160;";
70843     }
70844     return value;
70845 };/**
70846  * @class Ext.grid.AbstractSelectionModel
70847  * @extends Ext.util.Observable
70848  * Abstract base class for grid SelectionModels.  It provides the interface that should be
70849  * implemented by descendant classes.  This class should not be directly instantiated.
70850  * @constructor
70851  */
70852 Ext.grid.AbstractSelectionModel = Ext.extend(Ext.util.Observable,  {
70853     /**
70854      * The GridPanel for which this SelectionModel is handling selection. Read-only.
70855      * @type Object
70856      * @property grid
70857      */
70858
70859     constructor : function(){
70860         this.locked = false;
70861         Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);
70862     },
70863
70864     /** @ignore Called by the grid automatically. Do not call directly. */
70865     init : function(grid){
70866         this.grid = grid;
70867         if(this.lockOnInit){
70868             delete this.lockOnInit;
70869             this.locked = false;
70870             this.lock();
70871         }
70872         this.initEvents();
70873     },
70874
70875     /**
70876      * Locks the selections.
70877      */
70878     lock : function(){
70879         if(!this.locked){
70880             this.locked = true;
70881             // If the grid has been set, then the view is already initialized.
70882             var g = this.grid;
70883             if(g){
70884                 g.getView().on({
70885                     scope: this,
70886                     beforerefresh: this.sortUnLock,
70887                     refresh: this.sortLock
70888                 });
70889             }else{
70890                 this.lockOnInit = true;
70891             }
70892         }
70893     },
70894
70895     // set the lock states before and after a view refresh
70896     sortLock : function() {
70897         this.locked = true;
70898     },
70899
70900     // set the lock states before and after a view refresh
70901     sortUnLock : function() {
70902         this.locked = false;
70903     },
70904
70905     /**
70906      * Unlocks the selections.
70907      */
70908     unlock : function(){
70909         if(this.locked){
70910             this.locked = false;
70911             var g = this.grid,
70912                 gv;
70913                 
70914             // If the grid has been set, then the view is already initialized.
70915             if(g){
70916                 gv = g.getView();
70917                 gv.un('beforerefresh', this.sortUnLock, this);
70918                 gv.un('refresh', this.sortLock, this);    
70919             }else{
70920                 delete this.lockOnInit;
70921             }
70922         }
70923     },
70924
70925     /**
70926      * Returns true if the selections are locked.
70927      * @return {Boolean}
70928      */
70929     isLocked : function(){
70930         return this.locked;
70931     },
70932
70933     destroy: function(){
70934         this.unlock();
70935         this.purgeListeners();
70936     }
70937 });/**
70938  * @class Ext.grid.RowSelectionModel
70939  * @extends Ext.grid.AbstractSelectionModel
70940  * The default SelectionModel used by {@link Ext.grid.GridPanel}.
70941  * It supports multiple selections and keyboard selection/navigation. The objects stored
70942  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
70943  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
70944  * @constructor
70945  * @param {Object} config
70946  */
70947 Ext.grid.RowSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
70948     /**
70949      * @cfg {Boolean} singleSelect
70950      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>
70951      * allowing multiple selections)
70952      */
70953     singleSelect : false,
70954     
70955     constructor : function(config){
70956         Ext.apply(this, config);
70957         this.selections = new Ext.util.MixedCollection(false, function(o){
70958             return o.id;
70959         });
70960
70961         this.last = false;
70962         this.lastActive = false;
70963
70964         this.addEvents(
70965                 /**
70966                  * @event selectionchange
70967                  * Fires when the selection changes
70968                  * @param {SelectionModel} this
70969                  */
70970                 'selectionchange',
70971                 /**
70972                  * @event beforerowselect
70973                  * Fires before a row is selected, return false to cancel the selection.
70974                  * @param {SelectionModel} this
70975                  * @param {Number} rowIndex The index to be selected
70976                  * @param {Boolean} keepExisting False if other selections will be cleared
70977                  * @param {Record} record The record to be selected
70978                  */
70979                 'beforerowselect',
70980                 /**
70981                  * @event rowselect
70982                  * Fires when a row is selected.
70983                  * @param {SelectionModel} this
70984                  * @param {Number} rowIndex The selected index
70985                  * @param {Ext.data.Record} r The selected record
70986                  */
70987                 'rowselect',
70988                 /**
70989                  * @event rowdeselect
70990                  * Fires when a row is deselected.  To prevent deselection
70991                  * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}. 
70992                  * @param {SelectionModel} this
70993                  * @param {Number} rowIndex
70994                  * @param {Record} record
70995                  */
70996                 'rowdeselect'
70997         );
70998         Ext.grid.RowSelectionModel.superclass.constructor.call(this);
70999     },
71000
71001     /**
71002      * @cfg {Boolean} moveEditorOnEnter
71003      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed
71004      * or the next row up when shift + enter keys are pressed.
71005      */
71006     // private
71007     initEvents : function(){
71008
71009         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
71010             this.grid.on('rowmousedown', this.handleMouseDown, this);
71011         }
71012
71013         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
71014             'up' : function(e){
71015                 if(!e.shiftKey || this.singleSelect){
71016                     this.selectPrevious(false);
71017                 }else if(this.last !== false && this.lastActive !== false){
71018                     var last = this.last;
71019                     this.selectRange(this.last,  this.lastActive-1);
71020                     this.grid.getView().focusRow(this.lastActive);
71021                     if(last !== false){
71022                         this.last = last;
71023                     }
71024                 }else{
71025                     this.selectFirstRow();
71026                 }
71027             },
71028             'down' : function(e){
71029                 if(!e.shiftKey || this.singleSelect){
71030                     this.selectNext(false);
71031                 }else if(this.last !== false && this.lastActive !== false){
71032                     var last = this.last;
71033                     this.selectRange(this.last,  this.lastActive+1);
71034                     this.grid.getView().focusRow(this.lastActive);
71035                     if(last !== false){
71036                         this.last = last;
71037                     }
71038                 }else{
71039                     this.selectFirstRow();
71040                 }
71041             },
71042             scope: this
71043         });
71044
71045         this.grid.getView().on({
71046             scope: this,
71047             refresh: this.onRefresh,
71048             rowupdated: this.onRowUpdated,
71049             rowremoved: this.onRemove
71050         });
71051     },
71052
71053     // private
71054     onRefresh : function(){
71055         var ds = this.grid.store, index;
71056         var s = this.getSelections();
71057         this.clearSelections(true);
71058         for(var i = 0, len = s.length; i < len; i++){
71059             var r = s[i];
71060             if((index = ds.indexOfId(r.id)) != -1){
71061                 this.selectRow(index, true);
71062             }
71063         }
71064         if(s.length != this.selections.getCount()){
71065             this.fireEvent('selectionchange', this);
71066         }
71067     },
71068
71069     // private
71070     onRemove : function(v, index, r){
71071         if(this.selections.remove(r) !== false){
71072             this.fireEvent('selectionchange', this);
71073         }
71074     },
71075
71076     // private
71077     onRowUpdated : function(v, index, r){
71078         if(this.isSelected(r)){
71079             v.onRowSelect(index);
71080         }
71081     },
71082
71083     /**
71084      * Select records.
71085      * @param {Array} records The records to select
71086      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
71087      */
71088     selectRecords : function(records, keepExisting){
71089         if(!keepExisting){
71090             this.clearSelections();
71091         }
71092         var ds = this.grid.store;
71093         for(var i = 0, len = records.length; i < len; i++){
71094             this.selectRow(ds.indexOf(records[i]), true);
71095         }
71096     },
71097
71098     /**
71099      * Gets the number of selected rows.
71100      * @return {Number}
71101      */
71102     getCount : function(){
71103         return this.selections.length;
71104     },
71105
71106     /**
71107      * Selects the first row in the grid.
71108      */
71109     selectFirstRow : function(){
71110         this.selectRow(0);
71111     },
71112
71113     /**
71114      * Select the last row.
71115      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
71116      */
71117     selectLastRow : function(keepExisting){
71118         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
71119     },
71120
71121     /**
71122      * Selects the row immediately following the last selected row.
71123      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
71124      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>
71125      */
71126     selectNext : function(keepExisting){
71127         if(this.hasNext()){
71128             this.selectRow(this.last+1, keepExisting);
71129             this.grid.getView().focusRow(this.last);
71130             return true;
71131         }
71132         return false;
71133     },
71134
71135     /**
71136      * Selects the row that precedes the last selected row.
71137      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
71138      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>
71139      */
71140     selectPrevious : function(keepExisting){
71141         if(this.hasPrevious()){
71142             this.selectRow(this.last-1, keepExisting);
71143             this.grid.getView().focusRow(this.last);
71144             return true;
71145         }
71146         return false;
71147     },
71148
71149     /**
71150      * Returns true if there is a next record to select
71151      * @return {Boolean}
71152      */
71153     hasNext : function(){
71154         return this.last !== false && (this.last+1) < this.grid.store.getCount();
71155     },
71156
71157     /**
71158      * Returns true if there is a previous record to select
71159      * @return {Boolean}
71160      */
71161     hasPrevious : function(){
71162         return !!this.last;
71163     },
71164
71165
71166     /**
71167      * Returns the selected records
71168      * @return {Array} Array of selected records
71169      */
71170     getSelections : function(){
71171         return [].concat(this.selections.items);
71172     },
71173
71174     /**
71175      * Returns the first selected record.
71176      * @return {Record}
71177      */
71178     getSelected : function(){
71179         return this.selections.itemAt(0);
71180     },
71181
71182     /**
71183      * Calls the passed function with each selection. If the function returns
71184      * <tt>false</tt>, iteration is stopped and this function returns
71185      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.
71186      * @param {Function} fn The function to call upon each iteration. It is passed the selected {@link Ext.data.Record Record}.
71187      * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this RowSelectionModel.
71188      * @return {Boolean} true if all selections were iterated
71189      */
71190     each : function(fn, scope){
71191         var s = this.getSelections();
71192         for(var i = 0, len = s.length; i < len; i++){
71193             if(fn.call(scope || this, s[i], i) === false){
71194                 return false;
71195             }
71196         }
71197         return true;
71198     },
71199
71200     /**
71201      * Clears all selections if the selection model
71202      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
71203      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the
71204      * conditional checks and events described in {@link #deselectRow}.
71205      */
71206     clearSelections : function(fast){
71207         if(this.isLocked()){
71208             return;
71209         }
71210         if(fast !== true){
71211             var ds = this.grid.store;
71212             var s = this.selections;
71213             s.each(function(r){
71214                 this.deselectRow(ds.indexOfId(r.id));
71215             }, this);
71216             s.clear();
71217         }else{
71218             this.selections.clear();
71219         }
71220         this.last = false;
71221     },
71222
71223
71224     /**
71225      * Selects all rows if the selection model
71226      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}. 
71227      */
71228     selectAll : function(){
71229         if(this.isLocked()){
71230             return;
71231         }
71232         this.selections.clear();
71233         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
71234             this.selectRow(i, true);
71235         }
71236     },
71237
71238     /**
71239      * Returns <tt>true</tt> if there is a selection.
71240      * @return {Boolean}
71241      */
71242     hasSelection : function(){
71243         return this.selections.length > 0;
71244     },
71245
71246     /**
71247      * Returns <tt>true</tt> if the specified row is selected.
71248      * @param {Number/Record} index The record or index of the record to check
71249      * @return {Boolean}
71250      */
71251     isSelected : function(index){
71252         var r = Ext.isNumber(index) ? this.grid.store.getAt(index) : index;
71253         return (r && this.selections.key(r.id) ? true : false);
71254     },
71255
71256     /**
71257      * Returns <tt>true</tt> if the specified record id is selected.
71258      * @param {String} id The id of record to check
71259      * @return {Boolean}
71260      */
71261     isIdSelected : function(id){
71262         return (this.selections.key(id) ? true : false);
71263     },
71264
71265     // private
71266     handleMouseDown : function(g, rowIndex, e){
71267         if(e.button !== 0 || this.isLocked()){
71268             return;
71269         }
71270         var view = this.grid.getView();
71271         if(e.shiftKey && !this.singleSelect && this.last !== false){
71272             var last = this.last;
71273             this.selectRange(last, rowIndex, e.ctrlKey);
71274             this.last = last; // reset the last
71275             view.focusRow(rowIndex);
71276         }else{
71277             var isSelected = this.isSelected(rowIndex);
71278             if(e.ctrlKey && isSelected){
71279                 this.deselectRow(rowIndex);
71280             }else if(!isSelected || this.getCount() > 1){
71281                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
71282                 view.focusRow(rowIndex);
71283             }
71284         }
71285     },
71286
71287     /**
71288      * Selects multiple rows.
71289      * @param {Array} rows Array of the indexes of the row to select
71290      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep
71291      * existing selections (defaults to <tt>false</tt>)
71292      */
71293     selectRows : function(rows, keepExisting){
71294         if(!keepExisting){
71295             this.clearSelections();
71296         }
71297         for(var i = 0, len = rows.length; i < len; i++){
71298             this.selectRow(rows[i], true);
71299         }
71300     },
71301
71302     /**
71303      * Selects a range of rows if the selection model
71304      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.
71305      * All rows in between startRow and endRow are also selected.
71306      * @param {Number} startRow The index of the first row in the range
71307      * @param {Number} endRow The index of the last row in the range
71308      * @param {Boolean} keepExisting (optional) True to retain existing selections
71309      */
71310     selectRange : function(startRow, endRow, keepExisting){
71311         var i;
71312         if(this.isLocked()){
71313             return;
71314         }
71315         if(!keepExisting){
71316             this.clearSelections();
71317         }
71318         if(startRow <= endRow){
71319             for(i = startRow; i <= endRow; i++){
71320                 this.selectRow(i, true);
71321             }
71322         }else{
71323             for(i = startRow; i >= endRow; i--){
71324                 this.selectRow(i, true);
71325             }
71326         }
71327     },
71328
71329     /**
71330      * Deselects a range of rows if the selection model
71331      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.  
71332      * All rows in between startRow and endRow are also deselected.
71333      * @param {Number} startRow The index of the first row in the range
71334      * @param {Number} endRow The index of the last row in the range
71335      */
71336     deselectRange : function(startRow, endRow, preventViewNotify){
71337         if(this.isLocked()){
71338             return;
71339         }
71340         for(var i = startRow; i <= endRow; i++){
71341             this.deselectRow(i, preventViewNotify);
71342         }
71343     },
71344
71345     /**
71346      * Selects a row.  Before selecting a row, checks if the selection model
71347      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
71348      * {@link #beforerowselect} event.  If these checks are satisfied the row
71349      * will be selected and followed up by  firing the {@link #rowselect} and
71350      * {@link #selectionchange} events.
71351      * @param {Number} row The index of the row to select
71352      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
71353      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
71354      * prevent notifying the view (disables updating the selected appearance)
71355      */
71356     selectRow : function(index, keepExisting, preventViewNotify){
71357         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
71358             return;
71359         }
71360         var r = this.grid.store.getAt(index);
71361         if(r && this.fireEvent('beforerowselect', this, index, keepExisting, r) !== false){
71362             if(!keepExisting || this.singleSelect){
71363                 this.clearSelections();
71364             }
71365             this.selections.add(r);
71366             this.last = this.lastActive = index;
71367             if(!preventViewNotify){
71368                 this.grid.getView().onRowSelect(index);
71369             }
71370             this.fireEvent('rowselect', this, index, r);
71371             this.fireEvent('selectionchange', this);
71372         }
71373     },
71374
71375     /**
71376      * Deselects a row.  Before deselecting a row, checks if the selection model
71377      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.
71378      * If this check is satisfied the row will be deselected and followed up by
71379      * firing the {@link #rowdeselect} and {@link #selectionchange} events.
71380      * @param {Number} row The index of the row to deselect
71381      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
71382      * prevent notifying the view (disables updating the selected appearance)
71383      */
71384     deselectRow : function(index, preventViewNotify){
71385         if(this.isLocked()){
71386             return;
71387         }
71388         if(this.last == index){
71389             this.last = false;
71390         }
71391         if(this.lastActive == index){
71392             this.lastActive = false;
71393         }
71394         var r = this.grid.store.getAt(index);
71395         if(r){
71396             this.selections.remove(r);
71397             if(!preventViewNotify){
71398                 this.grid.getView().onRowDeselect(index);
71399             }
71400             this.fireEvent('rowdeselect', this, index, r);
71401             this.fireEvent('selectionchange', this);
71402         }
71403     },
71404
71405     // private
71406     restoreLast : function(){
71407         if(this._last){
71408             this.last = this._last;
71409         }
71410     },
71411
71412     // private
71413     acceptsNav : function(row, col, cm){
71414         return !cm.isHidden(col) && cm.isCellEditable(col, row);
71415     },
71416
71417     // private
71418     onEditorKey : function(field, e){
71419         var k = e.getKey(), 
71420             newCell, 
71421             g = this.grid, 
71422             last = g.lastEdit,
71423             ed = g.activeEditor,
71424             ae, last, r, c;
71425         var shift = e.shiftKey;
71426         if(k == e.TAB){
71427             e.stopEvent();
71428             ed.completeEdit();
71429             if(shift){
71430                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
71431             }else{
71432                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
71433             }
71434         }else if(k == e.ENTER){
71435             if(this.moveEditorOnEnter !== false){
71436                 if(shift){
71437                     newCell = g.walkCells(last.row - 1, last.col, -1, this.acceptsNav, this);
71438                 }else{
71439                     newCell = g.walkCells(last.row + 1, last.col, 1, this.acceptsNav, this);
71440                 }
71441             }
71442         }
71443         if(newCell){
71444             r = newCell[0];
71445             c = newCell[1];
71446
71447             if(last.row != r){
71448                 this.selectRow(r); // *** highlight newly-selected cell and update selection
71449             }
71450
71451             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
71452                 ae = g.activeEditor;
71453                 if(ae && ae.field.triggerBlur){
71454                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
71455                     ae.field.triggerBlur();
71456                 }
71457             }
71458             g.startEditing(r, c);
71459         }
71460     },
71461     
71462     destroy : function(){
71463         if(this.rowNav){
71464             this.rowNav.disable();
71465             this.rowNav = null;
71466         }
71467         Ext.grid.RowSelectionModel.superclass.destroy.call(this);
71468     }
71469 });/**
71470  * @class Ext.grid.Column
71471  * <p>This class encapsulates column configuration data to be used in the initialization of a
71472  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
71473  * <p>While subclasses are provided to render data in different ways, this class renders a passed
71474  * data field unchanged and is usually used for textual columns.</p>
71475  */
71476 Ext.grid.Column = Ext.extend(Object, {
71477     /**
71478      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured
71479      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.
71480      * The initial configuration may be dynamically altered using
71481      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.
71482      */
71483     /**
71484      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial
71485      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all
71486      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be
71487      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>
71488      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>
71489      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>
71490      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this
71491      * unique identifier.
71492      */
71493     /**
71494      * @cfg {String} header Optional. The header text to be used as innerHTML
71495      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to
71496      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
71497      */
71498     /**
71499      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71500      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,
71501      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the
71502      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.
71503      */
71504     /**
71505      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71506      * may be used to specify the text with which to prefix the group field value in the group header line.
71507      * See also {@link #groupRenderer} and
71508      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.
71509      */
71510     /**
71511      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71512      * may be used to specify the function used to format the grouping field value for display in the group
71513      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured
71514      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified
71515      * the new value of the group field will be used.</p>
71516      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be
71517      * passed the following parameters:
71518      * <div class="mdetail-params"><ul>
71519      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>
71520      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>
71521      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data
71522      * for the row which caused group change.</p></li>
71523      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
71524      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>
71525      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
71526      * </ul></div></p>
71527      * <p>The function should return a string value.</p>
71528      */
71529     /**
71530      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
71531      * may be used to specify the text to display when there is an empty group value. Defaults to the
71532      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.
71533      */
71534     /**
71535      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
71536      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
71537      * which to draw the column's value.</p>
71538      */
71539     /**
71540      * @cfg {Number} width
71541      * Optional. The initial width in pixels of the column.
71542      * The width of each column can also be affected if any of the following are configured:
71543      * <div class="mdetail-params"><ul>
71544      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>
71545      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>
71546      * <div class="sub-desc">
71547      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be
71548      * re-proportioned (based on the relative initial widths) to fill the width of the grid so
71549      * that no horizontal scrollbar is shown.</p>
71550      * </div></li>
71551      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>
71552      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>
71553      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side
71554      * is reserved for the vertical scrollbar.  The
71555      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>
71556      * can be modified to reduce or eliminate the reserved offset.</p>
71557      */
71558     /**
71559      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
71560      * Defaults to the value of the <code>{@link Ext.grid.ColumnModel#defaultSortable}</code> property.
71561      * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.
71562      */
71563     /**
71564      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.
71565      */
71566     /**
71567      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.
71568      */
71569     /**
71570      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.
71571      */
71572     /**
71573      * @cfg {Boolean} hidden
71574      * Optional. <tt>true</tt> to initially hide this column. Defaults to <tt>false</tt>.
71575      * A hidden column {@link Ext.grid.GridPanel#enableColumnHide may be shown via the header row menu}.
71576      * If a column is never to be shown, simply do not include this column in the Column Model at all.
71577      */
71578     /**
71579      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips
71580      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the
71581      * header's HTML title attribute. Defaults to ''.
71582      */
71583     /**
71584      * @cfg {Mixed} renderer
71585      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>
71586      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,
71587      * appearance, etc.) before it is rendered). This may be specified in either of three ways:
71588      * <div class="mdetail-params"><ul>
71589      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>
71590      * <li>A string which references a property name of the {@link Ext.util.Format} class which
71591      * provides a renderer function.</li>
71592      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>
71593      * reference) e.g.:<pre style="margin-left:1.2em"><code>
71594 {
71595     fn: this.gridRenderer,
71596     scope: this
71597 }
71598 </code></pre></li></ul></div>
71599      * If not specified, the default renderer uses the raw data value.</p>
71600      * <p>For information about the renderer function (passed parameters, etc.), see
71601      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>
71602 var companyColumn = {
71603    header: 'Company Name',
71604    dataIndex: 'company',
71605    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
71606       // provide the logic depending on business rules
71607       // name of your own choosing to manipulate the cell depending upon
71608       // the data in the underlying Record object.
71609       if (value == 'whatever') {
71610           //metaData.css : String : A CSS class name to add to the TD element of the cell.
71611           //metaData.attr : String : An html attribute definition string to apply to
71612           //                         the data container element within the table
71613           //                         cell (e.g. 'style="color:red;"').
71614           metaData.css = 'name-of-css-class-you-will-define';
71615       }
71616       return value;
71617    }
71618 }
71619      * </code></pre>
71620      * See also {@link #scope}.
71621      */
71622     /**
71623      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass
71624      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily
71625      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:
71626      * <div class="mdetail-params"><ul>
71627      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>
71628      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>
71629      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>
71630      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>
71631      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>
71632      * </ul></div>
71633      * <p>Configuration properties for the specified <code>xtype</code> may be specified with
71634      * the Column configuration properties, for example:</p>
71635      * <pre><code>
71636 var grid = new Ext.grid.GridPanel({
71637     ...
71638     columns: [{
71639         header: 'Last Updated',
71640         dataIndex: 'lastChange',
71641         width: 85,
71642         sortable: true,
71643         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),
71644         xtype: 'datecolumn', // use xtype instead of renderer
71645         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}
71646     }, {
71647         ...
71648     }]
71649 });
71650      * </code></pre>
71651      */
71652     /**
71653      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the
71654      * renderer.  Defaults to the Column configuration object.
71655      */
71656     /**
71657      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.
71658      */
71659     /**
71660      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column
71661      * (excluding headers). Defaults to undefined.
71662      */
71663     /**
71664      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
71665      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use
71666      * {@link Ext.grid.GridPanel#enableColumnHide} instead.
71667      */
71668     /**
71669      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column
71670      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.
71671      */
71672
71673     /**
71674      * @private
71675      * @cfg {Boolean} isColumn
71676      * Used by ColumnModel setConfig method to avoid reprocessing a Column
71677      * if <code>isColumn</code> is not set ColumnModel will recreate a new Ext.grid.Column
71678      * Defaults to true.
71679      */
71680     isColumn : true,
71681
71682     constructor : function(config){
71683         Ext.apply(this, config);
71684
71685         if(Ext.isString(this.renderer)){
71686             this.renderer = Ext.util.Format[this.renderer];
71687         }else if(Ext.isObject(this.renderer)){
71688             this.scope = this.renderer.scope;
71689             this.renderer = this.renderer.fn;
71690         }
71691         if(!this.scope){
71692             this.scope = this;
71693         }
71694
71695         var ed = this.editor;
71696         delete this.editor;
71697         this.setEditor(ed);
71698     },
71699
71700     /**
71701      * Optional. A function which returns displayable data when passed the following parameters:
71702      * <div class="mdetail-params"><ul>
71703      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
71704      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
71705      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
71706      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container
71707      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>
71708      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was
71709      * extracted.</p></li>
71710      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
71711      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
71712      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record
71713      * was extracted.</p></li>
71714      * </ul></div>
71715      * @property renderer
71716      * @type Function
71717      */
71718     renderer : function(value){
71719         if(Ext.isString(value) && value.length < 1){
71720             return '&#160;';
71721         }
71722         return value;
71723     },
71724
71725     // private
71726     getEditor: function(rowIndex){
71727         return this.editable !== false ? this.editor : null;
71728     },
71729
71730     /**
71731      * Sets a new editor for this column.
71732      * @param {Ext.Editor/Ext.form.Field} editor The editor to set
71733      */
71734     setEditor : function(editor){
71735         var ed = this.editor;
71736         if(ed){
71737             if(ed.gridEditor){
71738                 ed.gridEditor.destroy();
71739                 delete ed.gridEditor;
71740             }else{
71741                 ed.destroy();
71742             }
71743         }
71744         this.editor = null;
71745         if(editor){
71746             //not an instance, create it
71747             if(!editor.isXType){
71748                 editor = Ext.create(editor, 'textfield');
71749             }
71750             this.editor = editor;
71751         }
71752     },
71753
71754     /**
71755      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}
71756      * used to edit the cell.
71757      * @param {Number} rowIndex The row index
71758      * @return {Ext.Editor}
71759      */
71760     getCellEditor: function(rowIndex){
71761         var ed = this.getEditor(rowIndex);
71762         if(ed){
71763             if(!ed.startEdit){
71764                 if(!ed.gridEditor){
71765                     ed.gridEditor = new Ext.grid.GridEditor(ed);
71766                 }
71767                 ed = ed.gridEditor;
71768             }
71769         }
71770         return ed;
71771     }
71772 });
71773
71774 /**
71775  * @class Ext.grid.BooleanColumn
71776  * @extends Ext.grid.Column
71777  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.Column#xtype xtype}
71778  * config option of {@link Ext.grid.Column} for more details.</p>
71779  */
71780 Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {
71781     /**
71782      * @cfg {String} trueText
71783      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
71784      */
71785     trueText: 'true',
71786     /**
71787      * @cfg {String} falseText
71788      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
71789      * <tt>'false'</tt>).
71790      */
71791     falseText: 'false',
71792     /**
71793      * @cfg {String} undefinedText
71794      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
71795      */
71796     undefinedText: '&#160;',
71797
71798     constructor: function(cfg){
71799         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);
71800         var t = this.trueText, f = this.falseText, u = this.undefinedText;
71801         this.renderer = function(v){
71802             if(v === undefined){
71803                 return u;
71804             }
71805             if(!v || v === 'false'){
71806                 return f;
71807             }
71808             return t;
71809         };
71810     }
71811 });
71812
71813 /**
71814  * @class Ext.grid.NumberColumn
71815  * @extends Ext.grid.Column
71816  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
71817  * {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more details.</p>
71818  */
71819 Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {
71820     /**
71821      * @cfg {String} format
71822      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
71823      * (defaults to <tt>'0,000.00'</tt>).
71824      */
71825     format : '0,000.00',
71826     constructor: function(cfg){
71827         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);
71828         this.renderer = Ext.util.Format.numberRenderer(this.format);
71829     }
71830 });
71831
71832 /**
71833  * @class Ext.grid.DateColumn
71834  * @extends Ext.grid.Column
71835  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
71836  * {@link #format}. See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column}
71837  * for more details.</p>
71838  */
71839 Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {
71840     /**
71841      * @cfg {String} format
71842      * A formatting string as used by {@link Date#format} to format a Date for this Column
71843      * (defaults to <tt>'m/d/Y'</tt>).
71844      */
71845     format : 'm/d/Y',
71846     constructor: function(cfg){
71847         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);
71848         this.renderer = Ext.util.Format.dateRenderer(this.format);
71849     }
71850 });
71851
71852 /**
71853  * @class Ext.grid.TemplateColumn
71854  * @extends Ext.grid.Column
71855  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s
71856  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
71857  * See the {@link Ext.grid.Column#xtype xtype} config option of {@link Ext.grid.Column} for more
71858  * details.</p>
71859  */
71860 Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {
71861     /**
71862      * @cfg {String/XTemplate} tpl
71863      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
71864      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.
71865      */
71866     constructor: function(cfg){
71867         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);
71868         var tpl = (!Ext.isPrimitive(this.tpl) && this.tpl.compile) ? this.tpl : new Ext.XTemplate(this.tpl);
71869         this.renderer = function(value, p, r){
71870             return tpl.apply(r.data);
71871         };
71872         this.tpl = tpl;
71873     }
71874 });
71875
71876 /*
71877  * @property types
71878  * @type Object
71879  * @member Ext.grid.Column
71880  * @static
71881  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced
71882  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>
71883  * <p>This contains the following properties</p><div class="mdesc-details"><ul>
71884  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>
71885  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>
71886  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>
71887  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>
71888  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>
71889  * </ul></div>
71890  */
71891 Ext.grid.Column.types = {
71892     gridcolumn : Ext.grid.Column,
71893     booleancolumn: Ext.grid.BooleanColumn,
71894     numbercolumn: Ext.grid.NumberColumn,
71895     datecolumn: Ext.grid.DateColumn,
71896     templatecolumn: Ext.grid.TemplateColumn
71897 };/**
71898  * @class Ext.grid.RowNumberer
71899  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides
71900  * an automatic row numbering column.
71901  * <br>Usage:<br>
71902  <pre><code>
71903  // This is a typical column config with the first column providing row numbers
71904  var colModel = new Ext.grid.ColumnModel([
71905     new Ext.grid.RowNumberer(),
71906     {header: "Name", width: 80, sortable: true},
71907     {header: "Code", width: 50, sortable: true},
71908     {header: "Description", width: 200, sortable: true}
71909  ]);
71910  </code></pre>
71911  * @constructor
71912  * @param {Object} config The configuration options
71913  */
71914 Ext.grid.RowNumberer = Ext.extend(Object, {
71915     /**
71916      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row
71917      * number column (defaults to '').
71918      */
71919     header: "",
71920     /**
71921      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
71922      */
71923     width: 23,
71924     /**
71925      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
71926      * @hide
71927      */
71928     sortable: false,
71929     
71930     constructor : function(config){
71931         Ext.apply(this, config);
71932         if(this.rowspan){
71933             this.renderer = this.renderer.createDelegate(this);
71934         }
71935     },
71936
71937     // private
71938     fixed:true,
71939     hideable: false,
71940     menuDisabled:true,
71941     dataIndex: '',
71942     id: 'numberer',
71943     rowspan: undefined,
71944
71945     // private
71946     renderer : function(v, p, record, rowIndex){
71947         if(this.rowspan){
71948             p.cellAttr = 'rowspan="'+this.rowspan+'"';
71949         }
71950         return rowIndex+1;
71951     }
71952 });/**
71953  * @class Ext.grid.CheckboxSelectionModel
71954  * @extends Ext.grid.RowSelectionModel
71955  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
71956  * @constructor
71957  * @param {Object} config The configuration options
71958  */
71959 Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
71960
71961     /**
71962      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
71963      * checkbox column (defaults to <tt>false</tt>).
71964      */
71965     /**
71966      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the
71967      * checkbox column.  Defaults to:<pre><code>
71968      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>
71969      * </code></pre>
71970      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header
71971      * and provides support for automatic check all/none behavior on header click. This string
71972      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,
71973      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the
71974      * <tt>'x-grid3-hd-checker'</tt> class is supplied.
71975      */
71976     header : '<div class="x-grid3-hd-checker">&#160;</div>',
71977     /**
71978      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).
71979      */
71980     width : 20,
71981     /**
71982      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to
71983      * <tt>false</tt>).
71984      */
71985     sortable : false,
71986
71987     // private
71988     menuDisabled : true,
71989     fixed : true,
71990     hideable: false,
71991     dataIndex : '',
71992     id : 'checker',
71993
71994     constructor : function(){
71995         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);
71996
71997         if(this.checkOnly){
71998             this.handleMouseDown = Ext.emptyFn;
71999         }
72000     },
72001
72002     // private
72003     initEvents : function(){
72004         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
72005         this.grid.on('render', function(){
72006             var view = this.grid.getView();
72007             view.mainBody.on('mousedown', this.onMouseDown, this);
72008             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
72009
72010         }, this);
72011     },
72012
72013     // If handleMouseDown was called from another event (enableDragDrop), set a flag so
72014     // onMouseDown does not process it a second time
72015     handleMouseDown : function() {
72016         Ext.grid.CheckboxSelectionModel.superclass.handleMouseDown.apply(this, arguments);
72017         this.mouseHandled = true;
72018     },
72019
72020     // private
72021     onMouseDown : function(e, t){
72022         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
72023             e.stopEvent();
72024             var row = e.getTarget('.x-grid3-row');
72025
72026             // mouseHandled flag check for a duplicate selection (handleMouseDown) call
72027             if(!this.mouseHandled && row){
72028                 var index = row.rowIndex;
72029                 if(this.isSelected(index)){
72030                     this.deselectRow(index);
72031                 }else{
72032                     this.selectRow(index, true);
72033                     this.grid.getView().focusRow(index);
72034                 }
72035             }
72036         }
72037         this.mouseHandled = false;
72038     },
72039
72040     // private
72041     onHdMouseDown : function(e, t){
72042         if(t.className == 'x-grid3-hd-checker'){
72043             e.stopEvent();
72044             var hd = Ext.fly(t.parentNode);
72045             var isChecked = hd.hasClass('x-grid3-hd-checker-on');
72046             if(isChecked){
72047                 hd.removeClass('x-grid3-hd-checker-on');
72048                 this.clearSelections();
72049             }else{
72050                 hd.addClass('x-grid3-hd-checker-on');
72051                 this.selectAll();
72052             }
72053         }
72054     },
72055
72056     // private
72057     renderer : function(v, p, record){
72058         return '<div class="x-grid3-row-checker">&#160;</div>';
72059     }
72060 });/**
72061  * @class Ext.grid.CellSelectionModel
72062  * @extends Ext.grid.AbstractSelectionModel
72063  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.
72064  * The object stored as the selection contains the following properties:
72065  * <div class="mdetail-params"><ul>
72066  * <li><b>cell</b> : see {@link #getSelectedCell} 
72067  * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}
72068  * which provides the data for the row containing the selection</li>
72069  * </ul></div>
72070  * @constructor
72071  * @param {Object} config The object containing the configuration of this model.
72072  */
72073 Ext.grid.CellSelectionModel = Ext.extend(Ext.grid.AbstractSelectionModel,  {
72074     
72075     constructor : function(config){
72076         Ext.apply(this, config);
72077
72078             this.selection = null;
72079         
72080             this.addEvents(
72081                 /**
72082                  * @event beforecellselect
72083                  * Fires before a cell is selected, return false to cancel the selection.
72084                  * @param {SelectionModel} this
72085                  * @param {Number} rowIndex The selected row index
72086                  * @param {Number} colIndex The selected cell index
72087                  */
72088                 "beforecellselect",
72089                 /**
72090                  * @event cellselect
72091                  * Fires when a cell is selected.
72092                  * @param {SelectionModel} this
72093                  * @param {Number} rowIndex The selected row index
72094                  * @param {Number} colIndex The selected cell index
72095                  */
72096                 "cellselect",
72097                 /**
72098                  * @event selectionchange
72099                  * Fires when the active selection changes.
72100                  * @param {SelectionModel} this
72101                  * @param {Object} selection null for no selection or an object with two properties
72102                  * <div class="mdetail-params"><ul>
72103                  * <li><b>cell</b> : see {@link #getSelectedCell} 
72104                  * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}
72105                  * which provides the data for the row containing the selection</p></li>
72106                  * </ul></div>
72107                  */
72108                 "selectionchange"
72109             );
72110         
72111             Ext.grid.CellSelectionModel.superclass.constructor.call(this);
72112     },
72113
72114     /** @ignore */
72115     initEvents : function(){
72116         this.grid.on('cellmousedown', this.handleMouseDown, this);
72117         this.grid.on(Ext.EventManager.useKeydown ? 'keydown' : 'keypress', this.handleKeyDown, this);
72118         this.grid.getView().on({
72119             scope: this,
72120             refresh: this.onViewChange,
72121             rowupdated: this.onRowUpdated,
72122             beforerowremoved: this.clearSelections,
72123             beforerowsinserted: this.clearSelections
72124         });
72125         if(this.grid.isEditor){
72126             this.grid.on('beforeedit', this.beforeEdit,  this);
72127         }
72128     },
72129
72130         //private
72131     beforeEdit : function(e){
72132         this.select(e.row, e.column, false, true, e.record);
72133     },
72134
72135         //private
72136     onRowUpdated : function(v, index, r){
72137         if(this.selection && this.selection.record == r){
72138             v.onCellSelect(index, this.selection.cell[1]);
72139         }
72140     },
72141
72142         //private
72143     onViewChange : function(){
72144         this.clearSelections(true);
72145     },
72146
72147         /**
72148      * Returns an array containing the row and column indexes of the currently selected cell
72149      * (e.g., [0, 0]), or null if none selected. The array has elements:
72150      * <div class="mdetail-params"><ul>
72151      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>
72152      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell. 
72153      * Due to possible column reordering, the cellIndex should <b>not</b> be used as an
72154      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>
72155      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code>
72156 // get name
72157 var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
72158 // get data value based on name
72159 var data = record.get(fieldName);
72160      * </code></pre></p></li>
72161      * </ul></div>
72162      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.
72163          */
72164     getSelectedCell : function(){
72165         return this.selection ? this.selection.cell : null;
72166     },
72167
72168     /**
72169      * If anything is selected, clears all selections and fires the selectionchange event.
72170      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from
72171      * being notified about the change.
72172      */
72173     clearSelections : function(preventNotify){
72174         var s = this.selection;
72175         if(s){
72176             if(preventNotify !== true){
72177                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
72178             }
72179             this.selection = null;
72180             this.fireEvent("selectionchange", this, null);
72181         }
72182     },
72183
72184     /**
72185      * Returns <tt>true</tt> if there is a selection.
72186      * @return {Boolean}
72187      */
72188     hasSelection : function(){
72189         return this.selection ? true : false;
72190     },
72191
72192     /** @ignore */
72193     handleMouseDown : function(g, row, cell, e){
72194         if(e.button !== 0 || this.isLocked()){
72195             return;
72196         }
72197         this.select(row, cell);
72198     },
72199
72200     /**
72201      * Selects a cell.  Before selecting a cell, fires the
72202      * {@link #beforecellselect} event.  If this check is satisfied the cell
72203      * will be selected and followed up by  firing the {@link #cellselect} and
72204      * {@link #selectionchange} events.
72205      * @param {Number} rowIndex The index of the row to select
72206      * @param {Number} colIndex The index of the column to select
72207      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
72208      * prevent notifying the view (disables updating the selected appearance)
72209      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at
72210      * the specified rowIndex / colIndex from being focused.
72211      * @param {Ext.data.Record} r (optional) The record to select
72212      */
72213     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
72214         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
72215             this.clearSelections();
72216             r = r || this.grid.store.getAt(rowIndex);
72217             this.selection = {
72218                 record : r,
72219                 cell : [rowIndex, colIndex]
72220             };
72221             if(!preventViewNotify){
72222                 var v = this.grid.getView();
72223                 v.onCellSelect(rowIndex, colIndex);
72224                 if(preventFocus !== true){
72225                     v.focusCell(rowIndex, colIndex);
72226                 }
72227             }
72228             this.fireEvent("cellselect", this, rowIndex, colIndex);
72229             this.fireEvent("selectionchange", this, this.selection);
72230         }
72231     },
72232
72233         //private
72234     isSelectable : function(rowIndex, colIndex, cm){
72235         return !cm.isHidden(colIndex);
72236     },
72237     
72238     // private
72239     onEditorKey: function(field, e){
72240         if(e.getKey() == e.TAB){
72241             this.handleKeyDown(e);
72242         }
72243     },
72244
72245     /** @ignore */
72246     handleKeyDown : function(e){
72247         if(!e.isNavKeyPress()){
72248             return;
72249         }
72250         
72251         var k = e.getKey(),
72252             g = this.grid,
72253             s = this.selection,
72254             sm = this,
72255             walk = function(row, col, step){
72256                 return g.walkCells(
72257                     row,
72258                     col,
72259                     step,
72260                     g.isEditor && g.editing ? sm.acceptsNav : sm.isSelectable, // *** handle tabbing while editorgrid is in edit mode
72261                     sm
72262                 );
72263             },
72264             cell, newCell, r, c, ae;
72265
72266         switch(k){
72267             case e.ESC:
72268             case e.PAGE_UP:
72269             case e.PAGE_DOWN:
72270                 // do nothing
72271                 break;
72272             default:
72273                 // *** call e.stopEvent() only for non ESC, PAGE UP/DOWN KEYS
72274                 e.stopEvent();
72275                 break;
72276         }
72277
72278         if(!s){
72279             cell = walk(0, 0, 1); // *** use private walk() function defined above
72280             if(cell){
72281                 this.select(cell[0], cell[1]);
72282             }
72283             return;
72284         }
72285
72286         cell = s.cell;  // currently selected cell
72287         r = cell[0];    // current row
72288         c = cell[1];    // current column
72289         
72290         switch(k){
72291             case e.TAB:
72292                 if(e.shiftKey){
72293                     newCell = walk(r, c - 1, -1);
72294                 }else{
72295                     newCell = walk(r, c + 1, 1);
72296                 }
72297                 break;
72298             case e.DOWN:
72299                 newCell = walk(r + 1, c, 1);
72300                 break;
72301             case e.UP:
72302                 newCell = walk(r - 1, c, -1);
72303                 break;
72304             case e.RIGHT:
72305                 newCell = walk(r, c + 1, 1);
72306                 break;
72307             case e.LEFT:
72308                 newCell = walk(r, c - 1, -1);
72309                 break;
72310             case e.ENTER:
72311                 if (g.isEditor && !g.editing) {
72312                     g.startEditing(r, c);
72313                     return;
72314                 }
72315                 break;
72316         }
72317
72318         if(newCell){
72319             // *** reassign r & c variables to newly-selected cell's row and column
72320             r = newCell[0];
72321             c = newCell[1];
72322
72323             this.select(r, c); // *** highlight newly-selected cell and update selection
72324
72325             if(g.isEditor && g.editing){ // *** handle tabbing while editorgrid is in edit mode
72326                 ae = g.activeEditor;
72327                 if(ae && ae.field.triggerBlur){
72328                     // *** if activeEditor is a TriggerField, explicitly call its triggerBlur() method
72329                     ae.field.triggerBlur();
72330                 }
72331                 g.startEditing(r, c);
72332             }
72333         }
72334     },
72335
72336     acceptsNav : function(row, col, cm){
72337         return !cm.isHidden(col) && cm.isCellEditable(col, row);
72338     }
72339 });/**
72340  * @class Ext.grid.EditorGridPanel
72341  * @extends Ext.grid.GridPanel
72342  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing
72343  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing
72344  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>
72345  * <p>Editability of columns may be controlled programatically by inserting an implementation
72346  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the
72347  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
72348  * <p>Editing is performed on the value of the <i>field</i> specified by the column's
72349  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}
72350  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display
72351  * transformed data, this must be accounted for).</p>
72352  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}
72353  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}
72354  * mapping would be an appropriate editor.</p>
72355  * If there is a more complex mismatch between the visible data in the grid, and the editable data in
72356  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be
72357  * injected using the {@link #beforeedit} and {@link #afteredit} events.
72358  * @constructor
72359  * @param {Object} config The config object
72360  * @xtype editorgrid
72361  */
72362 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {
72363     /**
72364      * @cfg {Number} clicksToEdit
72365      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>
72366      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts
72367      * editing that cell.</p>
72368      */
72369     clicksToEdit: 2,
72370
72371     /**
72372     * @cfg {Boolean} forceValidation
72373     * True to force validation even if the value is unmodified (defaults to false)
72374     */
72375     forceValidation: false,
72376
72377     // private
72378     isEditor : true,
72379     // private
72380     detectEdit: false,
72381
72382     /**
72383      * @cfg {Boolean} autoEncode
72384      * True to automatically HTML encode and decode values pre and post edit (defaults to false)
72385      */
72386     autoEncode : false,
72387
72388     /**
72389      * @cfg {Boolean} trackMouseOver @hide
72390      */
72391     // private
72392     trackMouseOver: false, // causes very odd FF errors
72393
72394     // private
72395     initComponent : function(){
72396         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);
72397
72398         if(!this.selModel){
72399             /**
72400              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for
72401              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).
72402              */
72403             this.selModel = new Ext.grid.CellSelectionModel();
72404         }
72405
72406         this.activeEditor = null;
72407
72408         this.addEvents(
72409             /**
72410              * @event beforeedit
72411              * Fires before cell editing is triggered. The edit event object has the following properties <br />
72412              * <ul style="padding:5px;padding-left:16px;">
72413              * <li>grid - This grid</li>
72414              * <li>record - The record being edited</li>
72415              * <li>field - The field name being edited</li>
72416              * <li>value - The value for the field being edited.</li>
72417              * <li>row - The grid row index</li>
72418              * <li>column - The grid column index</li>
72419              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
72420              * </ul>
72421              * @param {Object} e An edit event (see above for description)
72422              */
72423             "beforeedit",
72424             /**
72425              * @event afteredit
72426              * Fires after a cell is edited. The edit event object has the following properties <br />
72427              * <ul style="padding:5px;padding-left:16px;">
72428              * <li>grid - This grid</li>
72429              * <li>record - The record being edited</li>
72430              * <li>field - The field name being edited</li>
72431              * <li>value - The value being set</li>
72432              * <li>originalValue - The original value for the field, before the edit.</li>
72433              * <li>row - The grid row index</li>
72434              * <li>column - The grid column index</li>
72435              * </ul>
72436              *
72437              * <pre><code>
72438 grid.on('afteredit', afterEdit, this );
72439
72440 function afterEdit(e) {
72441     // execute an XHR to send/commit data to the server, in callback do (if successful):
72442     e.record.commit();
72443 };
72444              * </code></pre>
72445              * @param {Object} e An edit event (see above for description)
72446              */
72447             "afteredit",
72448             /**
72449              * @event validateedit
72450              * Fires after a cell is edited, but before the value is set in the record. Return false
72451              * to cancel the change. The edit event object has the following properties <br />
72452              * <ul style="padding:5px;padding-left:16px;">
72453              * <li>grid - This grid</li>
72454              * <li>record - The record being edited</li>
72455              * <li>field - The field name being edited</li>
72456              * <li>value - The value being set</li>
72457              * <li>originalValue - The original value for the field, before the edit.</li>
72458              * <li>row - The grid row index</li>
72459              * <li>column - The grid column index</li>
72460              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
72461              * </ul>
72462              * Usage example showing how to remove the red triangle (dirty record indicator) from some
72463              * records (not all).  By observing the grid's validateedit event, it can be cancelled if
72464              * the edit occurs on a targeted row (for example) and then setting the field's new value
72465              * in the Record directly:
72466              * <pre><code>
72467 grid.on('validateedit', function(e) {
72468   var myTargetRow = 6;
72469
72470   if (e.row == myTargetRow) {
72471     e.cancel = true;
72472     e.record.data[e.field] = e.value;
72473   }
72474 });
72475              * </code></pre>
72476              * @param {Object} e An edit event (see above for description)
72477              */
72478             "validateedit"
72479         );
72480     },
72481
72482     // private
72483     initEvents : function(){
72484         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);
72485
72486         this.getGridEl().on('mousewheel', this.stopEditing.createDelegate(this, [true]), this);
72487         this.on('columnresize', this.stopEditing, this, [true]);
72488
72489         if(this.clicksToEdit == 1){
72490             this.on("cellclick", this.onCellDblClick, this);
72491         }else {
72492             var view = this.getView();
72493             if(this.clicksToEdit == 'auto' && view.mainBody){
72494                 view.mainBody.on('mousedown', this.onAutoEditClick, this);
72495             }
72496             this.on('celldblclick', this.onCellDblClick, this);
72497         }
72498     },
72499
72500     onResize : function(){
72501         Ext.grid.EditorGridPanel.superclass.onResize.apply(this, arguments);
72502         var ae = this.activeEditor;
72503         if(this.editing && ae){
72504             ae.realign(true);
72505         }
72506     },
72507
72508     // private
72509     onCellDblClick : function(g, row, col){
72510         this.startEditing(row, col);
72511     },
72512
72513     // private
72514     onAutoEditClick : function(e, t){
72515         if(e.button !== 0){
72516             return;
72517         }
72518         var row = this.view.findRowIndex(t),
72519             col = this.view.findCellIndex(t);
72520         if(row !== false && col !== false){
72521             this.stopEditing();
72522             if(this.selModel.getSelectedCell){ // cell sm
72523                 var sc = this.selModel.getSelectedCell();
72524                 if(sc && sc[0] === row && sc[1] === col){
72525                     this.startEditing(row, col);
72526                 }
72527             }else{
72528                 if(this.selModel.isSelected(row)){
72529                     this.startEditing(row, col);
72530                 }
72531             }
72532         }
72533     },
72534
72535     // private
72536     onEditComplete : function(ed, value, startValue){
72537         this.editing = false;
72538         this.lastActiveEditor = this.activeEditor;
72539         this.activeEditor = null;
72540
72541         var r = ed.record,
72542             field = this.colModel.getDataIndex(ed.col);
72543         value = this.postEditValue(value, startValue, r, field);
72544         if(this.forceValidation === true || String(value) !== String(startValue)){
72545             var e = {
72546                 grid: this,
72547                 record: r,
72548                 field: field,
72549                 originalValue: startValue,
72550                 value: value,
72551                 row: ed.row,
72552                 column: ed.col,
72553                 cancel:false
72554             };
72555             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){
72556                 r.set(field, e.value);
72557                 delete e.cancel;
72558                 this.fireEvent("afteredit", e);
72559             }
72560         }
72561         this.view.focusCell(ed.row, ed.col);
72562     },
72563
72564     /**
72565      * Starts editing the specified for the specified row/column
72566      * @param {Number} rowIndex
72567      * @param {Number} colIndex
72568      */
72569     startEditing : function(row, col){
72570         this.stopEditing();
72571         if(this.colModel.isCellEditable(col, row)){
72572             this.view.ensureVisible(row, col, true);
72573             var r = this.store.getAt(row),
72574                 field = this.colModel.getDataIndex(col),
72575                 e = {
72576                     grid: this,
72577                     record: r,
72578                     field: field,
72579                     value: r.data[field],
72580                     row: row,
72581                     column: col,
72582                     cancel:false
72583                 };
72584             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
72585                 this.editing = true;
72586                 var ed = this.colModel.getCellEditor(col, row);
72587                 if(!ed){
72588                     return;
72589                 }
72590                 if(!ed.rendered){
72591                     ed.parentEl = this.view.getEditorParent(ed);
72592                     ed.on({
72593                         scope: this,
72594                         render: {
72595                             fn: function(c){
72596                                 c.field.focus(false, true);
72597                             },
72598                             single: true,
72599                             scope: this
72600                         },
72601                         specialkey: function(field, e){
72602                             this.getSelectionModel().onEditorKey(field, e);
72603                         },
72604                         complete: this.onEditComplete,
72605                         canceledit: this.stopEditing.createDelegate(this, [true])
72606                     });
72607                 }
72608                 Ext.apply(ed, {
72609                     row     : row,
72610                     col     : col,
72611                     record  : r
72612                 });
72613                 this.lastEdit = {
72614                     row: row,
72615                     col: col
72616                 };
72617                 this.activeEditor = ed;
72618                 // Set the selectSameEditor flag if we are reusing the same editor again and
72619                 // need to prevent the editor from firing onBlur on itself.
72620                 ed.selectSameEditor = (this.activeEditor == this.lastActiveEditor);
72621                 var v = this.preEditValue(r, field);
72622                 ed.startEdit(this.view.getCell(row, col).firstChild, Ext.isDefined(v) ? v : '');
72623
72624                 // Clear the selectSameEditor flag
72625                 (function(){
72626                     delete ed.selectSameEditor;
72627                 }).defer(50);
72628             }
72629         }
72630     },
72631
72632     // private
72633     preEditValue : function(r, field){
72634         var value = r.data[field];
72635         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlDecode(value) : value;
72636     },
72637
72638     // private
72639     postEditValue : function(value, originalValue, r, field){
72640         return this.autoEncode && Ext.isString(value) ? Ext.util.Format.htmlEncode(value) : value;
72641     },
72642
72643     /**
72644      * Stops any active editing
72645      * @param {Boolean} cancel (optional) True to cancel any changes
72646      */
72647     stopEditing : function(cancel){
72648         if(this.editing){
72649             // Store the lastActiveEditor to check if it is changing
72650             var ae = this.lastActiveEditor = this.activeEditor;
72651             if(ae){
72652                 ae[cancel === true ? 'cancelEdit' : 'completeEdit']();
72653                 this.view.focusCell(ae.row, ae.col);
72654             }
72655             this.activeEditor = null;
72656         }
72657         this.editing = false;
72658     }
72659 });
72660 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private
72661 // This is a support class used internally by the Grid components
72662 Ext.grid.GridEditor = function(field, config){
72663     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);
72664     field.monitorTab = false;
72665 };
72666
72667 Ext.extend(Ext.grid.GridEditor, Ext.Editor, {
72668     alignment: "tl-tl",
72669     autoSize: "width",
72670     hideEl : false,
72671     cls: "x-small-editor x-grid-editor",
72672     shim:false,
72673     shadow:false
72674 });/**
72675  * @class Ext.grid.PropertyRecord
72676  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the
72677  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be
72678  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}
72679  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records
72680  * can also be created explicitly as shwon below.  Example usage:
72681  * <pre><code>
72682 var rec = new Ext.grid.PropertyRecord({
72683     name: 'Birthday',
72684     value: new Date(Date.parse('05/26/1972'))
72685 });
72686 // Add record to an already populated grid
72687 grid.store.addSorted(rec);
72688 </code></pre>
72689  * @constructor
72690  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type
72691  * will be read automatically by the grid to determine the type of editor to use when displaying it.
72692  */
72693 Ext.grid.PropertyRecord = Ext.data.Record.create([
72694     {name:'name',type:'string'}, 'value'
72695 ]);
72696
72697 /**
72698  * @class Ext.grid.PropertyStore
72699  * @extends Ext.util.Observable
72700  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping
72701  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format
72702  * required for compatibility with the underlying store. Generally this class should not need to be used directly --
72703  * the grid's data should be accessed from the underlying store via the {@link #store} property.
72704  * @constructor
72705  * @param {Ext.grid.Grid} grid The grid this store will be bound to
72706  * @param {Object} source The source data config object
72707  */
72708 Ext.grid.PropertyStore = Ext.extend(Ext.util.Observable, {
72709     
72710     constructor : function(grid, source){
72711         this.grid = grid;
72712         this.store = new Ext.data.Store({
72713             recordType : Ext.grid.PropertyRecord
72714         });
72715         this.store.on('update', this.onUpdate,  this);
72716         if(source){
72717             this.setSource(source);
72718         }
72719         Ext.grid.PropertyStore.superclass.constructor.call(this);    
72720     },
72721     
72722     // protected - should only be called by the grid.  Use grid.setSource instead.
72723     setSource : function(o){
72724         this.source = o;
72725         this.store.removeAll();
72726         var data = [];
72727         for(var k in o){
72728             if(this.isEditableValue(o[k])){
72729                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));
72730             }
72731         }
72732         this.store.loadRecords({records: data}, {}, true);
72733     },
72734
72735     // private
72736     onUpdate : function(ds, record, type){
72737         if(type == Ext.data.Record.EDIT){
72738             var v = record.data.value;
72739             var oldValue = record.modified.value;
72740             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
72741                 this.source[record.id] = v;
72742                 record.commit();
72743                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
72744             }else{
72745                 record.reject();
72746             }
72747         }
72748     },
72749
72750     // private
72751     getProperty : function(row){
72752        return this.store.getAt(row);
72753     },
72754
72755     // private
72756     isEditableValue: function(val){
72757         return Ext.isPrimitive(val) || Ext.isDate(val);
72758     },
72759
72760     // private
72761     setValue : function(prop, value, create){
72762         var r = this.getRec(prop);
72763         if(r){
72764             r.set('value', value);
72765             this.source[prop] = value;
72766         }else if(create){
72767             // only create if specified.
72768             this.source[prop] = value;
72769             r = new Ext.grid.PropertyRecord({name: prop, value: value}, prop);
72770             this.store.add(r);
72771
72772         }
72773     },
72774     
72775     // private
72776     remove : function(prop){
72777         var r = this.getRec(prop);
72778         if(r){
72779             this.store.remove(r);
72780             delete this.source[prop];
72781         }
72782     },
72783     
72784     // private
72785     getRec : function(prop){
72786         return this.store.getById(prop);
72787     },
72788
72789     // protected - should only be called by the grid.  Use grid.getSource instead.
72790     getSource : function(){
72791         return this.source;
72792     }
72793 });
72794
72795 /**
72796  * @class Ext.grid.PropertyColumnModel
72797  * @extends Ext.grid.ColumnModel
72798  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.
72799  * @constructor
72800  * @param {Ext.grid.Grid} grid The grid this store will be bound to
72801  * @param {Object} source The source data config object
72802  */
72803 Ext.grid.PropertyColumnModel = Ext.extend(Ext.grid.ColumnModel, {
72804     // private - strings used for locale support
72805     nameText : 'Name',
72806     valueText : 'Value',
72807     dateFormat : 'm/j/Y',
72808     trueText: 'true',
72809     falseText: 'false',
72810     
72811     constructor : function(grid, store){
72812         var g = Ext.grid,
72813                 f = Ext.form;
72814                 
72815             this.grid = grid;
72816             g.PropertyColumnModel.superclass.constructor.call(this, [
72817                 {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},
72818                 {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}
72819             ]);
72820             this.store = store;
72821         
72822             var bfield = new f.Field({
72823                 autoCreate: {tag: 'select', children: [
72824                     {tag: 'option', value: 'true', html: this.trueText},
72825                     {tag: 'option', value: 'false', html: this.falseText}
72826                 ]},
72827                 getValue : function(){
72828                     return this.el.dom.value == 'true';
72829                 }
72830             });
72831             this.editors = {
72832                 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
72833                 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
72834                 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
72835                 'boolean' : new g.GridEditor(bfield, {
72836                     autoSize: 'both'
72837                 })
72838             };
72839             this.renderCellDelegate = this.renderCell.createDelegate(this);
72840             this.renderPropDelegate = this.renderProp.createDelegate(this);
72841     },
72842
72843     // private
72844     renderDate : function(dateVal){
72845         return dateVal.dateFormat(this.dateFormat);
72846     },
72847
72848     // private
72849     renderBool : function(bVal){
72850         return this[bVal ? 'trueText' : 'falseText'];
72851     },
72852
72853     // private
72854     isCellEditable : function(colIndex, rowIndex){
72855         return colIndex == 1;
72856     },
72857
72858     // private
72859     getRenderer : function(col){
72860         return col == 1 ?
72861             this.renderCellDelegate : this.renderPropDelegate;
72862     },
72863
72864     // private
72865     renderProp : function(v){
72866         return this.getPropertyName(v);
72867     },
72868
72869     // private
72870     renderCell : function(val, meta, rec){
72871         var renderer = this.grid.customRenderers[rec.get('name')];
72872         if(renderer){
72873             return renderer.apply(this, arguments);
72874         }
72875         var rv = val;
72876         if(Ext.isDate(val)){
72877             rv = this.renderDate(val);
72878         }else if(typeof val == 'boolean'){
72879             rv = this.renderBool(val);
72880         }
72881         return Ext.util.Format.htmlEncode(rv);
72882     },
72883
72884     // private
72885     getPropertyName : function(name){
72886         var pn = this.grid.propertyNames;
72887         return pn && pn[name] ? pn[name] : name;
72888     },
72889
72890     // private
72891     getCellEditor : function(colIndex, rowIndex){
72892         var p = this.store.getProperty(rowIndex),
72893             n = p.data.name, 
72894             val = p.data.value;
72895         if(this.grid.customEditors[n]){
72896             return this.grid.customEditors[n];
72897         }
72898         if(Ext.isDate(val)){
72899             return this.editors.date;
72900         }else if(typeof val == 'number'){
72901             return this.editors.number;
72902         }else if(typeof val == 'boolean'){
72903             return this.editors['boolean'];
72904         }else{
72905             return this.editors.string;
72906         }
72907     },
72908
72909     // inherit docs
72910     destroy : function(){
72911         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);
72912         for(var ed in this.editors){
72913             Ext.destroy(this.editors[ed]);
72914         }
72915     }
72916 });
72917
72918 /**
72919  * @class Ext.grid.PropertyGrid
72920  * @extends Ext.grid.EditorGridPanel
72921  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
72922  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
72923  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:
72924  * <pre><code>
72925 var grid = new Ext.grid.PropertyGrid({
72926     title: 'Properties Grid',
72927     autoHeight: true,
72928     width: 300,
72929     renderTo: 'grid-ct',
72930     source: {
72931         "(name)": "My Object",
72932         "Created": new Date(Date.parse('10/15/2006')),
72933         "Available": false,
72934         "Version": .01,
72935         "Description": "A test object"
72936     }
72937 });
72938 </code></pre>
72939  * @constructor
72940  * @param {Object} config The grid config object
72941  */
72942 Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
72943     /**
72944     * @cfg {Object} propertyNames An object containing property name/display name pairs.
72945     * If specified, the display name will be shown in the name column instead of the property name.
72946     */
72947     /**
72948     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
72949     */
72950     /**
72951     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
72952     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
72953     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
72954     * associated with a custom input control by specifying a custom editor.  The name of the editor
72955     * type should correspond with the name of the property that will use the editor.  Example usage:
72956     * <pre><code>
72957 var grid = new Ext.grid.PropertyGrid({
72958     ...
72959     customEditors: {
72960         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
72961     },
72962     source: {
72963         'Start Time': '10:00 AM'
72964     }
72965 });
72966 </code></pre>
72967     */
72968     /**
72969     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
72970     */
72971     /**
72972     * @cfg {Object} customRenderers An object containing name/value pairs of custom renderer type definitions that allow
72973     * the grid to support custom rendering of fields.  By default, the grid supports strongly-typed rendering
72974     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
72975     * associated with the type of the value.  The name of the renderer type should correspond with the name of the property
72976     * that it will render.  Example usage:
72977     * <pre><code>
72978 var grid = new Ext.grid.PropertyGrid({
72979     ...
72980     customRenderers: {
72981         Available: function(v){
72982             if(v){
72983                 return '<span style="color: green;">Yes</span>';
72984             }else{
72985                 return '<span style="color: red;">No</span>';
72986             }
72987         }
72988     },
72989     source: {
72990         Available: true
72991     }
72992 });
72993 </code></pre>
72994     */
72995
72996     // private config overrides
72997     enableColumnMove:false,
72998     stripeRows:false,
72999     trackMouseOver: false,
73000     clicksToEdit:1,
73001     enableHdMenu : false,
73002     viewConfig : {
73003         forceFit:true
73004     },
73005
73006     // private
73007     initComponent : function(){
73008         this.customRenderers = this.customRenderers || {};
73009         this.customEditors = this.customEditors || {};
73010         this.lastEditRow = null;
73011         var store = new Ext.grid.PropertyStore(this);
73012         this.propStore = store;
73013         var cm = new Ext.grid.PropertyColumnModel(this, store);
73014         store.store.sort('name', 'ASC');
73015         this.addEvents(
73016             /**
73017              * @event beforepropertychange
73018              * Fires before a property value changes.  Handlers can return false to cancel the property change
73019              * (this will internally call {@link Ext.data.Record#reject} on the property's record).
73020              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
73021              * as the {@link #source} config property).
73022              * @param {String} recordId The record's id in the data store
73023              * @param {Mixed} value The current edited property value
73024              * @param {Mixed} oldValue The original property value prior to editing
73025              */
73026             'beforepropertychange',
73027             /**
73028              * @event propertychange
73029              * Fires after a property value has changed.
73030              * @param {Object} source The source data object for the grid (corresponds to the same object passed in
73031              * as the {@link #source} config property).
73032              * @param {String} recordId The record's id in the data store
73033              * @param {Mixed} value The current edited property value
73034              * @param {Mixed} oldValue The original property value prior to editing
73035              */
73036             'propertychange'
73037         );
73038         this.cm = cm;
73039         this.ds = store.store;
73040         Ext.grid.PropertyGrid.superclass.initComponent.call(this);
73041
73042                 this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){
73043             if(colIndex === 0){
73044                 this.startEditing.defer(200, this, [rowIndex, 1]);
73045                 return false;
73046             }
73047         }, this);
73048     },
73049
73050     // private
73051     onRender : function(){
73052         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
73053
73054         this.getGridEl().addClass('x-props-grid');
73055     },
73056
73057     // private
73058     afterRender: function(){
73059         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
73060         if(this.source){
73061             this.setSource(this.source);
73062         }
73063     },
73064
73065     /**
73066      * Sets the source data object containing the property data.  The data object can contain one or more name/value
73067      * pairs representing all of the properties of an object to display in the grid, and this data will automatically
73068      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
73069      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
73070      * existing data.  See also the {@link #source} config value.  Example usage:
73071      * <pre><code>
73072 grid.setSource({
73073     "(name)": "My Object",
73074     "Created": new Date(Date.parse('10/15/2006')),  // date type
73075     "Available": false,  // boolean type
73076     "Version": .01,      // decimal type
73077     "Description": "A test object"
73078 });
73079 </code></pre>
73080      * @param {Object} source The data object
73081      */
73082     setSource : function(source){
73083         this.propStore.setSource(source);
73084     },
73085
73086     /**
73087      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
73088      * format of the data object.
73089      * @return {Object} The data object
73090      */
73091     getSource : function(){
73092         return this.propStore.getSource();
73093     },
73094     
73095     /**
73096      * Sets the value of a property.
73097      * @param {String} prop The name of the property to set
73098      * @param {Mixed} value The value to test
73099      * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to <tt>false</tt>.
73100      */
73101     setProperty : function(prop, value, create){
73102         this.propStore.setValue(prop, value, create);    
73103     },
73104     
73105     /**
73106      * Removes a property from the grid.
73107      * @param {String} prop The name of the property to remove
73108      */
73109     removeProperty : function(prop){
73110         this.propStore.remove(prop);
73111     }
73112
73113     /**
73114      * @cfg store
73115      * @hide
73116      */
73117     /**
73118      * @cfg colModel
73119      * @hide
73120      */
73121     /**
73122      * @cfg cm
73123      * @hide
73124      */
73125     /**
73126      * @cfg columns
73127      * @hide
73128      */
73129 });
73130 Ext.reg("propertygrid", Ext.grid.PropertyGrid);
73131 /**
73132  * @class Ext.grid.GroupingView
73133  * @extends Ext.grid.GridView
73134  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}
73135  * must be used to enable grouping.  Some grouping characteristics may also be configured at the
73136  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>
73137  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</code></li>
73138  * <li><code>{@link Ext.grid.Column#groupable groupable}</code></li>
73139  * <li><code>{@link Ext.grid.Column#groupName groupName}</code></li>
73140  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</code></li>
73141  * </ul></div>
73142  * <p>Sample usage:</p>
73143  * <pre><code>
73144 var grid = new Ext.grid.GridPanel({
73145     // A groupingStore is required for a GroupingView
73146     store: new {@link Ext.data.GroupingStore}({
73147         autoDestroy: true,
73148         reader: reader,
73149         data: xg.dummyData,
73150         sortInfo: {field: 'company', direction: 'ASC'},
73151         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,
73152         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,
73153         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'
73154     }),
73155     colModel: new {@link Ext.grid.ColumnModel}({
73156         columns:[
73157             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},
73158             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level
73159             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},
73160             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},
73161             {header: 'Industry', dataIndex: 'industry'},
73162             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
73163         ],
73164         defaults: {
73165             sortable: true,
73166             menuDisabled: false,
73167             width: 20
73168         }
73169     }),
73170
73171     view: new Ext.grid.GroupingView({
73172         {@link Ext.grid.GridView#forceFit forceFit}: true,
73173         // custom grouping text template to display the number of items per group
73174         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
73175     }),
73176
73177     frame:true,
73178     width: 700,
73179     height: 450,
73180     collapsible: true,
73181     animCollapse: false,
73182     title: 'Grouping Example',
73183     iconCls: 'icon-grid',
73184     renderTo: document.body
73185 });
73186  * </code></pre>
73187  * @constructor
73188  * @param {Object} config
73189  */
73190 Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {
73191
73192     /**
73193      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column
73194      * (defaults to 'Group By This Field').
73195      */
73196     groupByText : 'Group By This Field',
73197     /**
73198      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
73199      * (defaults to 'Show in Groups').
73200      */
73201     showGroupsText : 'Show in Groups',
73202     /**
73203      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)
73204      */
73205     hideGroupedColumn : false,
73206     /**
73207      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value
73208      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>
73209      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the
73210      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).
73211      */
73212     showGroupName : true,
73213     /**
73214      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
73215      */
73216     startCollapsed : false,
73217     /**
73218      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)
73219      */
73220     enableGrouping : true,
73221     /**
73222      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)
73223      */
73224     enableGroupingMenu : true,
73225     /**
73226      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
73227      */
73228     enableNoGroups : true,
73229     /**
73230      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).
73231      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.
73232      */
73233     emptyGroupText : '(None)',
73234     /**
73235      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)
73236      */
73237     ignoreAdd : false,
73238     /**
73239      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).
73240      * This is used to format an object which contains the following properties:
73241      * <div class="mdetail-params"><ul>
73242      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.
73243      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>
73244      * is specified, it is the result of a call to that function.</p></li>
73245      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>
73246      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>
73247      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>
73248      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the
73249      * View Element which contains the group.</p></li>
73250      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
73251      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data
73252      * for the row which caused group change.</p></li>
73253      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>
73254      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>
73255      * </ul></div></p>
73256      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>
73257 var grid = new Ext.grid.GridPanel({
73258     ...
73259     view: new Ext.grid.GroupingView({
73260         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
73261     }),
73262 });
73263      * </code></pre>
73264      */
73265     groupTextTpl : '{text}',
73266
73267     /**
73268      * @cfg {String} groupMode Indicates how to construct the group identifier. <tt>'value'</tt> constructs the id using
73269      * raw value, <tt>'display'</tt> constructs the id using the rendered value. Defaults to <tt>'value'</tt>.
73270      */
73271     groupMode: 'value',
73272
73273     /**
73274      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for
73275      * each column.
73276      */
73277
73278     // private
73279     initTemplates : function(){
73280         Ext.grid.GroupingView.superclass.initTemplates.call(this);
73281         this.state = {};
73282
73283         var sm = this.grid.getSelectionModel();
73284         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
73285                 this.onBeforeRowSelect, this);
73286
73287         if(!this.startGroup){
73288             this.startGroup = new Ext.XTemplate(
73289                 '<div id="{groupId}" class="x-grid-group {cls}">',
73290                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
73291                     '<div id="{groupId}-bd" class="x-grid-group-body">'
73292             );
73293         }
73294         this.startGroup.compile();
73295
73296         if (!this.endGroup) {
73297             this.endGroup = '</div></div>';
73298         }
73299     },
73300
73301     // private
73302     findGroup : function(el){
73303         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
73304     },
73305
73306     // private
73307     getGroups : function(){
73308         return this.hasRows() ? this.mainBody.dom.childNodes : [];
73309     },
73310
73311     // private
73312     onAdd : function(ds, records, index) {
73313         if (this.canGroup() && !this.ignoreAdd) {
73314             var ss = this.getScrollState();
73315             this.fireEvent('beforerowsinserted', ds, index, index + (records.length-1));
73316             this.refresh();
73317             this.restoreScroll(ss);
73318             this.fireEvent('rowsinserted', ds, index, index + (records.length-1));
73319         } else if (!this.canGroup()) {
73320             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);
73321         }
73322     },
73323
73324     // private
73325     onRemove : function(ds, record, index, isUpdate){
73326         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);
73327         var g = document.getElementById(record._groupId);
73328         if(g && g.childNodes[1].childNodes.length < 1){
73329             Ext.removeNode(g);
73330         }
73331         this.applyEmptyText();
73332     },
73333
73334     // private
73335     refreshRow : function(record){
73336         if(this.ds.getCount()==1){
73337             this.refresh();
73338         }else{
73339             this.isUpdating = true;
73340             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);
73341             this.isUpdating = false;
73342         }
73343     },
73344
73345     // private
73346     beforeMenuShow : function(){
73347         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;
73348         if((item = items.get('groupBy'))){
73349             item.setDisabled(disabled);
73350         }
73351         if((item = items.get('showGroups'))){
73352             item.setDisabled(disabled);
73353             item.setChecked(this.enableGrouping, true);
73354         }
73355     },
73356
73357     // private
73358     renderUI : function(){
73359         Ext.grid.GroupingView.superclass.renderUI.call(this);
73360         this.mainBody.on('mousedown', this.interceptMouse, this);
73361
73362         if(this.enableGroupingMenu && this.hmenu){
73363             this.hmenu.add('-',{
73364                 itemId:'groupBy',
73365                 text: this.groupByText,
73366                 handler: this.onGroupByClick,
73367                 scope: this,
73368                 iconCls:'x-group-by-icon'
73369             });
73370             if(this.enableNoGroups){
73371                 this.hmenu.add({
73372                     itemId:'showGroups',
73373                     text: this.showGroupsText,
73374                     checked: true,
73375                     checkHandler: this.onShowGroupsClick,
73376                     scope: this
73377                 });
73378             }
73379             this.hmenu.on('beforeshow', this.beforeMenuShow, this);
73380         }
73381     },
73382
73383     processEvent: function(name, e){
73384         Ext.grid.GroupingView.superclass.processEvent.call(this, name, e);
73385         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
73386         if(hd){
73387             // group value is at the end of the string
73388             var field = this.getGroupField(),
73389                 prefix = this.getPrefix(field),
73390                 groupValue = hd.id.substring(prefix.length);
73391
73392             // remove trailing '-hd'
73393             groupValue = groupValue.substr(0, groupValue.length - 3);
73394             if(groupValue){
73395                 this.grid.fireEvent('group' + name, this.grid, field, groupValue, e);
73396             }
73397         }
73398
73399     },
73400
73401     // private
73402     onGroupByClick : function(){
73403         this.enableGrouping = true;
73404         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
73405         this.grid.fireEvent('groupchange', this, this.grid.store.getGroupState());
73406         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
73407         this.refresh();
73408     },
73409
73410     // private
73411     onShowGroupsClick : function(mi, checked){
73412         this.enableGrouping = checked;
73413         if(checked){
73414             this.onGroupByClick();
73415         }else{
73416             this.grid.store.clearGrouping();
73417             this.grid.fireEvent('groupchange', this, null);
73418         }
73419     },
73420
73421     /**
73422      * Toggle the group that contains the specific row.
73423      * @param {Number} rowIndex The row inside the group
73424      * @param {Boolean} expanded (optional)
73425      */
73426     toggleRowIndex : function(rowIndex, expanded){
73427         if(!this.canGroup()){
73428             return;
73429         }
73430         var row = this.getRow(rowIndex);
73431         if(row){
73432             this.toggleGroup(this.findGroup(row), expanded);
73433         }
73434     },
73435
73436     /**
73437      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.
73438      * @param {String} groupId The groupId assigned to the group (see getGroupId)
73439      * @param {Boolean} expanded (optional)
73440      */
73441     toggleGroup : function(group, expanded){
73442         var gel = Ext.get(group);
73443         expanded = Ext.isDefined(expanded) ? expanded : gel.hasClass('x-grid-group-collapsed');
73444         if(this.state[gel.id] !== expanded){
73445             this.grid.stopEditing(true);
73446             this.state[gel.id] = expanded;
73447             gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
73448         }
73449     },
73450
73451     /**
73452      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.
73453      * @param {Boolean} expanded (optional)
73454      */
73455     toggleAllGroups : function(expanded){
73456         var groups = this.getGroups();
73457         for(var i = 0, len = groups.length; i < len; i++){
73458             this.toggleGroup(groups[i], expanded);
73459         }
73460     },
73461
73462     /**
73463      * Expands all grouped rows.
73464      */
73465     expandAllGroups : function(){
73466         this.toggleAllGroups(true);
73467     },
73468
73469     /**
73470      * Collapses all grouped rows.
73471      */
73472     collapseAllGroups : function(){
73473         this.toggleAllGroups(false);
73474     },
73475
73476     // private
73477     interceptMouse : function(e){
73478         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
73479         if(hd){
73480             e.stopEvent();
73481             this.toggleGroup(hd.parentNode);
73482         }
73483     },
73484
73485     // private
73486     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){
73487         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);
73488         if(g === '' || g === '&#160;'){
73489             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
73490         }
73491         return g;
73492     },
73493
73494     // private
73495     getGroupField : function(){
73496         return this.grid.store.getGroupState();
73497     },
73498
73499     // private
73500     afterRender : function(){
73501         if(!this.ds || !this.cm){
73502             return;
73503         }
73504         Ext.grid.GroupingView.superclass.afterRender.call(this);
73505         if(this.grid.deferRowRender){
73506             this.updateGroupWidths();
73507         }
73508     },
73509
73510     // private
73511     renderRows : function(){
73512         var groupField = this.getGroupField();
73513         var eg = !!groupField;
73514         // if they turned off grouping and the last grouped field is hidden
73515         if(this.hideGroupedColumn) {
73516             var colIndex = this.cm.findColumnIndex(groupField),
73517                 hasLastGroupField = Ext.isDefined(this.lastGroupField);
73518             if(!eg && hasLastGroupField){
73519                 this.mainBody.update('');
73520                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);
73521                 delete this.lastGroupField;
73522             }else if (eg && !hasLastGroupField){
73523                 this.lastGroupField = groupField;
73524                 this.cm.setHidden(colIndex, true);
73525             }else if (eg && hasLastGroupField && groupField !== this.lastGroupField) {
73526                 this.mainBody.update('');
73527                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
73528                 this.cm.setHidden(oldIndex, false);
73529                 this.lastGroupField = groupField;
73530                 this.cm.setHidden(colIndex, true);
73531             }
73532         }
73533         return Ext.grid.GroupingView.superclass.renderRows.apply(
73534                     this, arguments);
73535     },
73536
73537     // private
73538     doRender : function(cs, rs, ds, startRow, colCount, stripe){
73539         if(rs.length < 1){
73540             return '';
73541         }
73542
73543         if(!this.canGroup() || this.isUpdating){
73544             return Ext.grid.GroupingView.superclass.doRender.apply(this, arguments);
73545         }
73546
73547         var groupField = this.getGroupField(),
73548             colIndex = this.cm.findColumnIndex(groupField),
73549             g,
73550             gstyle = 'width:' + this.getTotalWidth() + ';',
73551             cfg = this.cm.config[colIndex],
73552             groupRenderer = cfg.groupRenderer || cfg.renderer,
73553             prefix = this.showGroupName ? (cfg.groupName || cfg.header)+': ' : '',
73554             groups = [],
73555             curGroup, i, len, gid;
73556
73557         for(i = 0, len = rs.length; i < len; i++){
73558             var rowIndex = startRow + i,
73559                 r = rs[i],
73560                 gvalue = r.data[groupField];
73561
73562                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);
73563             if(!curGroup || curGroup.group != g){
73564                 gid = this.constructId(gvalue, groupField, colIndex);
73565                 // if state is defined use it, however state is in terms of expanded
73566                 // so negate it, otherwise use the default.
73567                 this.state[gid] = !(Ext.isDefined(this.state[gid]) ? !this.state[gid] : this.startCollapsed);
73568                 curGroup = {
73569                     group: g,
73570                     gvalue: gvalue,
73571                     text: prefix + g,
73572                     groupId: gid,
73573                     startRow: rowIndex,
73574                     rs: [r],
73575                     cls: this.state[gid] ? '' : 'x-grid-group-collapsed',
73576                     style: gstyle
73577                 };
73578                 groups.push(curGroup);
73579             }else{
73580                 curGroup.rs.push(r);
73581             }
73582             r._groupId = gid;
73583         }
73584
73585         var buf = [];
73586         for(i = 0, len = groups.length; i < len; i++){
73587             g = groups[i];
73588             this.doGroupStart(buf, g, cs, ds, colCount);
73589             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(
73590                     this, cs, g.rs, ds, g.startRow, colCount, stripe);
73591
73592             this.doGroupEnd(buf, g, cs, ds, colCount);
73593         }
73594         return buf.join('');
73595     },
73596
73597     /**
73598      * Dynamically tries to determine the groupId of a specific value
73599      * @param {String} value
73600      * @return {String} The group id
73601      */
73602     getGroupId : function(value){
73603         var field = this.getGroupField();
73604         return this.constructId(value, field, this.cm.findColumnIndex(field));
73605     },
73606
73607     // private
73608     constructId : function(value, field, idx){
73609         var cfg = this.cm.config[idx],
73610             groupRenderer = cfg.groupRenderer || cfg.renderer,
73611             val = (this.groupMode == 'value') ? value : this.getGroup(value, {data:{}}, groupRenderer, 0, idx, this.ds);
73612
73613         return this.getPrefix(field) + Ext.util.Format.htmlEncode(val);
73614     },
73615
73616     // private
73617     canGroup  : function(){
73618         return this.enableGrouping && !!this.getGroupField();
73619     },
73620
73621     // private
73622     getPrefix: function(field){
73623         return this.grid.getGridEl().id + '-gp-' + field + '-';
73624     },
73625
73626     // private
73627     doGroupStart : function(buf, g, cs, ds, colCount){
73628         buf[buf.length] = this.startGroup.apply(g);
73629     },
73630
73631     // private
73632     doGroupEnd : function(buf, g, cs, ds, colCount){
73633         buf[buf.length] = this.endGroup;
73634     },
73635
73636     // private
73637     getRows : function(){
73638         if(!this.canGroup()){
73639             return Ext.grid.GroupingView.superclass.getRows.call(this);
73640         }
73641         var r = [],
73642             gs = this.getGroups(),
73643             g,
73644             i = 0,
73645             len = gs.length,
73646             j,
73647             jlen;
73648         for(; i < len; ++i){
73649             g = gs[i].childNodes[1];
73650             if(g){
73651                 g = g.childNodes;
73652                 for(j = 0, jlen = g.length; j < jlen; ++j){
73653                     r[r.length] = g[j];
73654                 }
73655             }
73656         }
73657         return r;
73658     },
73659
73660     // private
73661     updateGroupWidths : function(){
73662         if(!this.canGroup() || !this.hasRows()){
73663             return;
73664         }
73665         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.getScrollOffset()) +'px';
73666         var gs = this.getGroups();
73667         for(var i = 0, len = gs.length; i < len; i++){
73668             gs[i].firstChild.style.width = tw;
73669         }
73670     },
73671
73672     // private
73673     onColumnWidthUpdated : function(col, w, tw){
73674         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);
73675         this.updateGroupWidths();
73676     },
73677
73678     // private
73679     onAllColumnWidthsUpdated : function(ws, tw){
73680         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);
73681         this.updateGroupWidths();
73682     },
73683
73684     // private
73685     onColumnHiddenUpdated : function(col, hidden, tw){
73686         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);
73687         this.updateGroupWidths();
73688     },
73689
73690     // private
73691     onLayout : function(){
73692         this.updateGroupWidths();
73693     },
73694
73695     // private
73696     onBeforeRowSelect : function(sm, rowIndex){
73697         this.toggleRowIndex(rowIndex, true);
73698     }
73699 });
73700 // private
73701 Ext.grid.GroupingView.GROUP_ID = 1000;